1. 포인트샵 개요
DXCMS 포인트샵은 회원이 활동으로 적립한 포인트를 사용하여 뱃지•칭호•포인트 충전•기타 아이템을 구매할 수 있는 가상 상점 시스템입니다. 관리자는 [포인트샵 관리] 메뉴에서 상품을 등록•편집•비활성화하고, 구매 내역을 조회하며 필요 시 환불 처리를 수행합니다.
📌 접근 경로: 관리자 대시보드 → 포인트샵 관리 (URL: /admin/shop) 사용자 샵 페이지: /shop (사이트 프론트엔드)
1.1 포인트샵 시스템 구성
| 구성 요소 |
파일 경로 |
역할 |
| 관리자 화면 |
admin/shop/index.php |
상품 등록·수정·비활성화, 구매 내역 조회, 환불 처리. |
| DxShop 엔진 |
core/DxShop.php |
상품 조회·구매 처리·환불·회원 구매내역 조회·카테고리 목록 반환 등 비즈니스 로직. |
| 구매 API |
core/api/shop.php |
AJAX POST 구매 요청 처리. 로그인 확인·CSRF 검증 후 DxShop::purchase() 호출. |
| 사용자 샵 페이지 |
pages/shop.php |
회원이 실제 상품을 보고 구매하는 프론트엔드 페이지. /shop URL. |
| DB 테이블: shop_items |
shop_items |
상품 정보 저장. id/name/description/category/type/price/stock/buy_limit/image/value/status/sort_order/created_at 컬럼. |
| DB 테이블: shop_purchases |
shop_purchases |
구매 이력 저장. id/member_id/item_id/item_name/price/status/note/created_at 컬럼. |
1.2 관리자 화면 3탭 구성
| 탭 |
설명 |
| 📦 상품 리스트 |
등록된 모든 상품을 테이블로 표시. 수정·삭제 버튼 제공. |
| ➕ 신규 상품 등록 |
새 상품을 등록하는 폼 화면. 상품 유형·가격·재고·구매 제한·이미지·가치값·상태 설정. |
| 📋 구매/환불 이력 |
최근 50건의 전체 구매 내역. 구매자·상품명·결제 포인트·상태 표시. paid 상태에 환불 버튼 제공. |
💡 상단 오른쪽 [사용자 샵 ↗] 버튼으로 실제 사용자 화면(/shop)을 새 탭에서 미리 볼 수 있습니다.
2. 상품 유형 (type)
포인트샵의 모든 상품은 4가지 유형 중 하나로 분류됩니다. 상품 유형은 구매 후 후처리(dx_shop_after_purchase 훅)에서 어떤 효과를 줄지를 결정하는 핵심 분류입니다.
| 유형값 |
표시명 |
주요 사용 사례 |
value 필드 활용 예시 |
| badge |
🏅 뱃지 |
프로필에 표시되는 특수 뱃지 |
"badge-pro", "badge-veteran" — 구매 후 훅에서 회원 프로필에 해당 뱃지 코드를 저장. |
| title |
🎖 칭호 |
닉네임 옆에 표시되는 칭호 텍스트 |
"legendary-warrior", "golden-master" — 구매 후 훅에서 members 테이블의 title 컬럼 등에 저장. |
| point |
💰 포인트 충전 |
포인트를 다시 충전하는 상품 |
"1000", "5000" — 구매 후 훅에서 value만큼 DxPoint::add()로 포인트를 지급. |
| etc |
📦 기타 |
이모지 스티커, 전용 기능 등 |
"special-theme", "dark-mode-key" — 유연하게 커스텀 로직 적용. |
2.1 상품 유형과 value 필드의 관계
상품 유형(type)은 카테고리 분류이며, 실제 구매 후 어떤 처리를 할지는 value 필드의 값과 dx_shop_after_purchase 훅이 결정합니다.
💡 DXCMS 기본 설치에서는 dx_shop_after_purchase 훅이 아무 동작도 하지 않습니다. 실제 효과(뱃지 지급, 칭호 저장 등)를 구현하려면 플러그인에서 이 훅을 처리하는 코드를 작성해야 합니다. value 필드는 훅 함수에서 $data["item"]["value"]로 접근할 수 있습니다.
3. 상품 등록 및 수정
[신규 상품 등록] 탭 또는 상품 목록에서 [수정] 버튼을 클릭하면 상품 등록/수정 폼이 표시됩니다. 신규와 수정 폼의 구조는 동일하며, 수정 시 기존 값이 자동으로 채워집니다.
3.1 상품 등록 폼 — 전체 필드 설명
| 필드명 |
필수 여부 |
설명 및 유의사항 |
| 상품 명칭 (name) |
필수 |
사용자 샵 페이지에 표시되는 상품 이름. 미입력 시 "상품명을 입력하세요." 오류. DxSanitizer::text()로 XSS 방지. |
| 상품 요약 설명 (description) |
선택 |
상품 카드 하단에 표시되는 간략한 설명. 2줄 textarea. DxSanitizer::text() 처리. |
| 상품 유형 (type) |
필수 |
badge/title/point/etc 중 하나 선택. 드롭다운 형태. badge·title·point·etc 외 값이면 오류. |
| 카테고리 (category) |
선택 |
자유 텍스트 입력. 사용자 샵 필터 탭으로 자동 생성됨. 예: 한정판, 프리미엄, 시즌1. DxSanitizer::text() 처리. |
| 판매 가격 (price) |
필수 |
구매 시 차감될 포인트 수. 0 이상의 정수. 우측에 "P" 단위 표시. max(0, ...) 처리로 음수 불가. |
| 재고 / 무제한 체크박스 (stock) |
선택 |
[재고 무제한] 체크 시 stock=-1로 저장. 체크 해제 시 수량 입력. -1이면 품절 없음. 0이 되면 품절 처리. 기본값: 무제한. |
| 1인당 구매 제한 (buy_limit) |
선택 |
회원 1인이 구매할 수 있는 최대 횟수. 0이면 무제한. 예: 1이면 중복 구매 불가(1인 1구매 상품). |
| 정렬 순서 (sort_order) |
선택 |
낮을수록 상품 목록 상단에 표시. 기본값 0. 같은 sort_order이면 ID 오름차순. |
| 상품 이미지 URL (image) |
선택 |
상품 카드 썸네일 이미지 URL. DxSanitizer::url() 처리. 미입력 시 상품 유형별 기본 이모지 표시. |
| 지급 데이터 값 (value) |
선택 |
구매 후 훅(dx_shop_after_purchase)에서 사용할 식별값. 예: badge-pro, 1000. 모노스페이스 폰트 입력 창. DxSanitizer::text() 처리. |
| 판매 상태 (status) |
필수 |
판매중(1) 또는 숨김(0). 라디오 버튼 선택. 기본값: 판매중. 숨김 설정 시 사용자 샵에서 표시되지 않음. |
3.2 등록/수정 처리 흐름
- CSRF 토큰 검증 (dx_csrf_check())
- _act === "save_item" 확인
- 상품명 필수 검증 → 빈 값이면 "상품명을 입력하세요." 오류
- type 유효성 검증 → badge/title/point/etc 외 값이면 오류
- stock: stock_unlimited 체크 여부에 따라 -1 또는 max(0, 입력값) 처리
- 신규(item_id=0): shop_items 테이블에 INSERT, created_at 자동 설정
- 수정(item_id>0): shop_items 테이블에서 해당 id UPDATE
- "상품이 등록되었습니다." 또는 "상품이 수정되었습니다." 성공 메시지
- 자동으로 상품 목록(sub=items) 탭으로 이동
3.3 등록 절차
- [신규 상품 등록] 탭 클릭
- 상품 명칭 입력 (필수)
- 상품 요약 설명 입력 (선택)
- 상품 유형 선택: 뱃지/칭호/포인트 충전/기타
- 카테고리 입력 (선택, 예: 한정판)
- 판매 가격 입력 (P 단위)
- 재고 설정: [재고 무제한] 체크 또는 수량 입력
- 1인당 구매 제한 입력 (0=무제한)
- 정렬 순서 입력 (낮을수록 상단)
- 상품 이미지 URL 입력 (선택)
- 지급 데이터 값(value) 입력 (선택, 훅에서 사용)
- 판매 상태 선택: 판매중 또는 숨김
- [새로운 상품 등록 완료] 버튼 클릭
- 성공 메시지 확인 후 상품 목록에서 확인
4. 상품 목록 (상품 리스트 탭)
등록된 모든 상품이 sort_order ASC → id ASC 순으로 테이블에 표시됩니다. getItems(null, null, false)로 비활성(status=0) 상품도 포함하여 관리자에게 보여줍니다.
4.1 목록 컬럼 설명
| 컬럼 |
내용 및 설명 |
| ID |
#N 형식의 상품 고유 ID. 모노스페이스 이탤릭 회색 텍스트. |
| 상품 정보 |
상품 이미지(또는 박스 아이콘) + 상품명(굵은 글씨) + 상품 유형(DxShop::typeName()으로 한글 변환). 마우스 호버 시 상품명이 초록색으로 변경. |
| 카테고리 |
인디고색 pill 배지. 카테고리 미설정 시 "-" 표시. |
| 가격 |
주황색 굵은 모노스페이스 숫자 + "P" 단위. 천 단위 쉼표 포함. |
| 재고 |
stock=-1이면 파란색 "무제한" 배지. 그 외 숫자로 잔여 재고 표시. |
| 구매제한 |
buy_limit=0이면 "제한없음". 그 외 "N회" 형식. |
| 상태 |
판매중(초록 배지) / 중지됨(회색 배지). status=1이면 판매중. |
| 관리 |
[수정] 하늘색 펜 아이콘: /admin/shop?sub=edit&id=N으로 이동. [삭제] 빨간 쓰레기통 아이콘: 확인 창 후 status=0으로 비활성화. |
4.2 상품 삭제 처리
상품 목록에서 [삭제] 버튼을 클릭하면 "상품을 삭제(비활성) 처리하시겠습니까?" 확인 창이 표시됩니다.
⚠️ 포인트샵의 상품 삭제는 DB에서 완전히 제거하지 않고 status=0으로 비활성화합니다. 이는 기존 구매 이력과의 참조 관계를 보존하기 위한 소프트 딜리트(Soft Delete) 방식입니다.
| 삭제 처리 |
UPDATE shop_items SET status=0 WHERE id=N 실행. |
| 삭제 후 |
상품 목록에서 "중지됨" 상태로 표시. 사용자 샵에서는 표시되지 않음. |
| 복구 방법 |
[수정] 버튼으로 편집 폼에 들어가 status를 "판매중"으로 변경 후 저장. |
| 완전 삭제 |
완전 삭제가 필요하면 DB에서 직접 DELETE 쿼리를 실행해야 합니다. |
5. 구매 처리 흐름 (DxShop::purchase)
사용자가 /shop 페이지에서 [구매하기] 버튼을 클릭하면 AJAX로 /api/shop에 POST 요청을 보내고, DxShop::purchase()가 다음 순서로 처리합니다.
5.1 구매 처리 단계
- AJAX 요청 수신 (POST /api/shop, item_id 포함)
- 로그인 확인: 비로그인이면 "로그인이 필요합니다." 오류(HTTP 401)
- CSRF 토큰 검증 (dx_csrf_check())
- 상품 조회: shop_items WHERE id=? AND status=1. 없으면 "존재하지 않는 상품입니다." 오류
- 재고 확인: stock != -1 AND stock <= 0이면 "품절된 상품입니다." 오류
- 구매 한도 확인: buy_limit > 0이면 shop_purchases에서 해당 회원의 결제완료(paid) 구매 수 조회 → 한도 초과 시 오류
- 포인트 확인: DxPoint::getPoint(memberId) < price이면 "포인트가 부족합니다. (보유: NP, 필요: MP)" 오류
- 포인트 차감: DxPoint::add(memberId, "purchase", -price, "shop_item", itemId, "[상품명] 구매")
- 재고 감소: stock > 0이면 UPDATE shop_items SET stock=stock-1 WHERE id=? AND stock>0
- 구매 기록: shop_purchases INSERT (member_id, item_id, item_name, price, status="paid", created_at)
- 후처리 훅 실행: dx_run_hook("dx_shop_after_purchase", {member_id, item, purchase_id})
- {"success":true, "message":"[상품명] 구매가 완료되었습니다.", "purchase_id":N} JSON 반환
5.2 구매 실패 응답
| 오류 메시지 |
원인 |
| 로그인이 필요합니다. |
비로그인 상태에서 구매 시도. |
| 존재하지 않는 상품입니다. |
상품 ID가 없거나 status=0(비활성) 상품. |
| 품절된 상품입니다. |
stock=0인 상품. 재고 무제한(-1)은 해당 없음. |
| 구매 한도를 초과했습니다. |
buy_limit > 0이고 이미 해당 횟수만큼 구매한 경우. |
| 포인트가 부족합니다. |
현재 보유 포인트 < 상품 가격. 보유/필요 포인트가 메시지에 포함. |
5.3 사용자 샵 구매 UX
- /shop 페이지에서 [구매하기] 클릭 시 다음 순서로 처리됩니다.
- "[상품명]을(를) NP에 구매할까요?" JavaScript confirm 창 표시
- 확인 클릭 시 XMLHttpRequest로 POST /api/shop?item_id=N 전송 (CSRF 토큰 포함)
- 성공: 상단 보유 포인트 표시가 즉시 차감 반영 → "✅ [성공 메시지]" 토스트 알림 → 1.5초 후 페이지 새로고침
- 실패: "❌ [오류 메시지]" 토스트 알림 (하단 중앙 팝업, 3초 후 자동 소멸)
6. 구매/환불 이력 탭
[구매/환불 이력] 탭에서 최근 50건의 구매 내역을 조회하고 환불 처리합니다. DxShop::getAllPurchases(50)로 shop_purchases JOIN members 데이터를 조회합니다.
6.1 구매 이력 테이블 컬럼
| 컬럼 |
내용 및 설명 |
| 구매일시 |
shop_purchases.created_at. "MM.DD HH:MM:SS" 형식 표시. |
| 구매자 |
members.name(굵은 글씨) + members.login_id(아래 줄 모노스페이스). LEFT JOIN으로 회원 정보 연결. |
| 상품명 |
shop_purchases.item_name. 구매 시점의 상품 이름이 그대로 저장됨. 이후 상품명 변경해도 이력 보존. |
| 결제 포인트 |
shop_purchases.price. 주황색 굵은 숫자 + "P". 천 단위 쉼표. |
| 상태 |
"결제완료"(초록): status=paid. "환불"(회색): status=refunded. 그 외: status값 그대로 표시(주황). |
| 관리 |
status=paid인 경우만 [환불 처리] 주황 버튼 표시. 이미 환불된 경우 "-" 표시. |
6.2 환불 처리 상세
[환불 처리] 버튼 클릭 시 "환불 처리를 진행하시겠습니까? 지급된 아이템 회수 로직은 별도로 수행해야 할 수 있습니다." 확인 창이 표시됩니다.
6.3 환불 처리 흐름 (DxShop::refund)
- CSRF 토큰 검증
- shop_purchases WHERE id=? AND status="paid" 조회 → 없으면 "구매 내역을 찾을 수 없습니다." 오류
- 포인트 환불: DxPoint::add(member_id, "refund", +price, "shop_purchase", purchaseId, "[상품명] 환불 (메모)")
- 재고 복구: 상품의 stock >= 0이면(무제한 아니면) stock+1 증가
- 구매 상태 업데이트: UPDATE shop_purchases SET status="refunded", note=메모 WHERE id=N
- "환불이 완료되었습니다." 성공 메시지 표시
⚠️ 환불은 포인트만 반환합니다. 뱃지•칭호 등 지급된 아이템(효과)은 자동으로 회수되지 않습니다. 아이템 회수가 필요하면 [회원 관리]에서 해당 회원의 프로필을 수동으로 수정하거나, dx_shop_after_refund 훅(미구현 시 플러그인으로 추가)을 활용해야 합니다.
7. 사용자 샵 페이지 (/shop)
사용자가 실제로 포인트로 상품을 구매하는 프론트엔드 페이지입니다. 관리자 패널의 [사용자 샵 ↗] 버튼으로 미리보기할 수 있습니다.
7.1 사용자 샵 화면 구성
| 영역 |
설명 |
| 헤더 |
"🛒 포인트샵" 제목과 로그인 회원의 현재 보유 포인트 표시 (💰 NP). 비로그인 시 "로그인 후 구매" 버튼. |
| 필터 탭 |
"전체" + 상품 유형(뱃지/칭호/포인트/기타) + 카테고리별 필터 링크. URL 파라미터: ?type=badge, ?cat=한정판. |
| 상품 그리드 |
auto-fill minmax(200px) CSS Grid. 모바일에서 2열로 자동 전환. |
| 토스트 알림 |
구매 성공/실패 시 화면 하단 중앙에 3초간 표시되는 팝업 알림. |
7.2 상품 카드 구성
| 요소 |
설명 |
| 이미지 영역 |
image URL이 있으면 80×80 이미지. 없으면 상품 유형 이모지(🏅/🎖/💰/📦) 표시. |
| 카테고리 |
보라색 작은 텍스트. 없으면 미표시. |
| 상품 유형 |
회색 작은 텍스트. DxShop::typeName()으로 한글 변환. |
| 상품명·설명 |
굵은 상품명 + 회색 설명 텍스트. |
| 가격·재고 |
주황색 가격(NP) + 우측에 재고("무제한" 또는 "N개 남음" 또는 "품절"). |
| 구매 버튼 |
상태별 버튼: [구매하기](파랑, 포인트 충분) / [포인트 부족](회색, 비활성) / [로그인 후 구매](파랑) / [품절](회색, 비활성). 품절 상품은 카드 전체 opacity 55%. |
7.3 상품 필터 동작
| 전체 필터 (기본) |
?type, ?cat 파라미터 없음. DxShop::getItems(null, null, true)로 전체 활성 상품 조회. |
| 유형 필터 (?type=badge) |
DxShop::getItems(null, "badge", true)로 해당 유형 상품만 조회. |
| 카테고리 필터 (?cat=한정판) |
DxShop::getItems("한정판", null, true)로 해당 카테고리 상품만 조회. |
| 카테고리 목록 자동 생성 |
DxShop::getCategories()가 활성 상품에서 DISTINCT category를 조회하여 필터 탭 자동 생성. |
8. 구매 후처리 훅 (dx_shop_after_purchase)
상품 구매가 완료되면 DxShop::purchase() 내부에서 dx_run_hook("dx_shop_after_purchase", ...)이 실행됩니다. 이 훅에 실제 아이템 지급 로직을 플러그인으로 구현합니다.
8.1 훅 전달 데이터
| 키 |
타입 |
내용 |
| member_id |
int |
구매한 회원의 ID. |
| item |
array |
구매한 상품 정보 배열. id/name/type/value/price 등 shop_items 전체 컬럼. |
| purchase_id |
int |
생성된 shop_purchases 레코드의 ID. |
8.2 훅 활용 예시
type과 value를 조합하여 구매된 상품에 맞는 효과를 구현합니다.
| 상품 유형 |
훅에서 구현해야 할 로직 |
| badge |
$data["item"]["value"]에서 뱃지 코드(예: "badge-pro")를 읽어 members 테이블의 badge 컬럼(또는 별도 member_badges 테이블)에 해당 회원에게 저장. |
| title |
$data["item"]["value"]에서 칭호 텍스트(예: "legendary-warrior")를 읽어 members 테이블의 title 컬럼에 저장하거나 별도 칭호 시스템 처리. |
| point |
$data["item"]["value"]에서 충전할 포인트 수(예: "1000")를 읽어 DxPoint::add($memberId, "refund", 1000, ...)로 포인트 지급. |
| etc |
value 값에 따라 사이트 특화 커스텀 로직 실행. 예: 특정 게시판 접근 권한 부여, 테마 전환 키 저장 등. |
💡 훅 등록 예시: dx_add_hook("dx_shop_after_purchase", function($data) { if($data["item"]["type"]==="point") { DxPoint::add($data["member_id"], "refund", (int)$data["item"]["value"], ...); } });
9. DxShop API 레퍼런스 (개발자)
| 메서드 시그니처 |
설명 및 반환값 |
| DxShop::getItems($cat, $type, $onlyActive) |
상품 목록 조회. 카테고리·유형·활성 여부 필터. sort_order ASC, id ASC 정렬. 배열 반환. |
| DxShop::getItem($itemId) |
단일 상품 조회. id+status=1 조건. 없으면 null 반환. |
| DxShop::purchase($memberId, $itemId) |
구매 처리. {success, message, purchase_id?, item?} 배열 반환. |
| DxShop::refund($purchaseId, $adminNote) |
환불 처리. {success, message} 배열 반환. 관리자 전용. |
| DxShop::getMemberPurchases($memberId, $limit, $offset) |
회원 구매 이력 조회. shop_purchases LEFT JOIN shop_items. 마이페이지에서 활용. |
| DxShop::getMemberPurchaseCount($memberId) |
회원 전체 구매 건수 반환(int). |
| DxShop::getAllPurchases($limit, $offset, $itemId) |
전체 구매 이력 조회. shop_purchases LEFT JOIN members. 관리자 화면에서 활용. |
| DxShop::typeName($type) |
상품 유형 코드를 한글로 변환. badge→"🏅 뱃지", title→"🎖 칭호" 등. |
| DxShop::getCategories() |
활성 상품의 카테고리 목록 배열. 사용자 샵 필터 탭 생성에 사용. |
10. 전체 사용 절차
10.1 포인트샵 처음 운영 시작하기
- 관리자 대시보드 → [포인트샵 관리] 메뉴 클릭
- [신규 상품 등록] 탭 클릭
- 첫 번째 상품 등록 (이름•유형•가격 최소 입력)
- [새로운 상품 등록 완료] 클릭
- [상품 리스트] 탭에서 등록된 상품 확인
- [사용자 샵 ↗] 버튼으로 실제 화면 미리보기
- 필요시 카테고리별•유형별 상품 추가
- 회원들이 구매하면 [구매/환불 이력] 탭에서 내역 확인
10.2 환불 처리 절차
- [구매/환불 이력] 탭 클릭
- 환불할 구매 건의 [환불 처리] 버튼 클릭
- "환불 처리를 진행하시겠습니까?" 확인 창에서 [확인]
- "환불이 완료되었습니다." 성공 메시지 확인
- 해당 회원의 포인트 잔액이 복구되었는지 [포인트 관리]에서 확인
- 필요시 [회원 관리]에서 지급된 아이템(뱃지•칭호 등) 수동 회수
11. 자주 묻는 질문 (FAQ)
Q1. 상품을 구매했는데 아무 효과가 없습니다.
A. DXCMS 기본 설치에서 dx_shop_after_purchase 훅은 아무것도 하지 않습니다. 뱃지•칭호 지급 등 실제 효과는 이 훅을 처리하는 플러그인이 필요합니다. 개발자에게 해당 훅 구현을 의뢰하거나, 플러그인 마켓에서 관련 확장을 찾아보세요.
Q2. 삭제한 상품을 다시 활성화할 수 있나요?
A. 네. [상품 리스트]에서 "중지됨" 상태로 표시된 상품의 [수정] 버튼을 클릭하고, 판매 상태를 "판매중"으로 변경 후 저장하면 됩니다.
Q3. 환불 후 뱃지가 사라지지 않습니다.
A. 환불은 포인트만 반환합니다. 뱃지•칭호 등 지급된 아이템은 자동 회수되지 않습니다. 수동으로 [회원 관리]에서 해당 회원 프로필을 수정하거나, dx_shop_after_purchase 훅에서 환불 감지 로직을 구현해야 합니다.
Q4. 1인당 1번만 구매 가능한 상품을 만들려면?
A. 상품 등록 시 [1인당 구매 제한]을 1로 설정하세요. 해당 회원이 이미 paid 상태로 구매한 이력이 있으면 다음 구매 시도 시 "구매 한도를 초과했습니다." 오류가 발생합니다.
Q5. 재고가 0이 되면 어떻게 됩니까?
A. stock=0이 되면 DxShop::purchase()에서 "품절된 상품입니다." 오류로 구매가 차단됩니다. 사용자 샵 카드에서도 [품절] 비활성 버튼으로 표시되며 카드 전체가 반투명으로 변합니다.
Q6. 구매 이력이 50건을 초과하면 어떻게 되나요?
A. 관리자 화면의 [구매/환불 이력]은 최근 50건만 표시됩니다. 더 오래된 이력은 DB에서 직접 조회하거나, 개발자가 별도 페이지네이션 기능을 추가해야 합니다.
Q7. 포인트 충전형 상품(type=point)은 자동으로 포인트가 지급되나요?
A. 기본 설치에서는 자동 지급되지 않습니다. dx_shop_after_purchase 훅에서 item.type이 "point"일 때 item.value 값만큼 DxPoint::add()를 호출하는 코드를 플러그인으로 구현해야 합니다.
12. 용어 정리
| 용어 |
설명 |
| shop_items |
상품 정보 DB 테이블. id/name/description/category/type/price/stock/buy_limit/image/value/status/sort_order/created_at. |
| shop_purchases |
구매 이력 DB 테이블. id/member_id/item_id/item_name/price/status/note/created_at. |
| type |
상품 유형. badge(뱃지)/title(칭호)/point(포인트 충전)/etc(기타). |
| price |
상품 구매에 필요한 포인트 수. 구매 시 회원 포인트에서 차감. |
| stock |
재고 수량. -1이면 무제한. 0이면 품절. 구매 성공 시 1 감소. |
| buy_limit |
1인당 최대 구매 횟수. 0이면 무제한. 초과 시 구매 차단. |
| value |
구매 후 처리에 사용할 상품 고유 식별값. 훅에서 $data["item"]["value"]로 접근. |
| status (상품) |
상품 활성 여부. 1=판매중, 0=숨김(소프트 딜리트). |
| status (구매) |
구매 상태. paid=결제완료, refunded=환불. |
| sort_order |
상품 표시 순서. 낮을수록 상단. 동일 시 id ASC. |
| DxShop |
포인트샵 비즈니스 로직 담당 정적 클래스. |
| DxShop::purchase() |
구매 처리 메서드. 재고·한도·포인트 확인 후 차감·기록·훅 실행. |
| DxShop::refund() |
환불 처리 메서드. 포인트 반환·재고 복구·구매 상태 변경. |
| dx_shop_after_purchase |
구매 완료 후 실행되는 훅. 실제 아이템 지급 로직 구현에 사용. |
| dx_run_hook() |
등록된 훅 핸들러를 실행하는 DXCMS 함수. |
| dx_csrf_check() |
CSRF 토큰 검증 함수. 구매 API와 관리자 POST 모두에 적용. |
| 소프트 딜리트(Soft Delete) |
실제 DB 삭제 없이 status=0으로 비활성화. 이력 보존. |
| 카테고리 필터 |
DxShop::getCategories()가 활성 상품의 DISTINCT category를 반환하여 샵 필터 탭 자동 생성. |
| sp-toast |
사용자 샵 구매 결과 알림 팝업 요소. 3초 후 자동 소멸. |
| dxBuy() |
사용자 샵의 구매 버튼 JavaScript 함수. confirm 후 AJAX POST. |