create-entity-server 0.0.31 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/template/configs/database.json +18 -7
  3. package/template/entities/README.md +3 -3
  4. package/template/entities/System/Address/addr_dong.json +10115 -0
  5. package/template/entities/System/Address/addr_sido.json +39 -0
  6. package/template/entities/System/Address/addr_sigungu.json +1398 -0
  7. package/template/entities/System/Auth/account.json +0 -64
  8. package/template/entities/System/Auth/account_device.json +7 -22
  9. package/template/entities/System/Auth/account_login_log.json +3 -8
  10. package/template/entities/System/Auth/anon_device.json +56 -0
  11. package/template/scripts/reset-all.sh +78 -5
  12. package/template/scripts/run.sh +73 -27
  13. package/template/configs/auth/oauth.json +0 -40
  14. package/template/configs/auth/password.json +0 -45
  15. package/template/configs/keys/apns.p8.example +0 -7
  16. package/template/configs/keys/firebase.pem.example +0 -7
  17. package/template/configs/notification/push.json +0 -25
  18. package/template/entities/System/Auth/account_oauth.json +0 -74
  19. package/template/entities/System/Auth/identity_verification.json +0 -106
  20. package/template/entities/System/Auth/password_history.json +0 -19
  21. package/template/entities/System/Notification/alimtalk_log.json +0 -65
  22. package/template/entities/System/Notification/alimtalk_msg.json +0 -53
  23. package/template/entities/System/Notification/friendtalk_log.json +0 -89
  24. package/template/entities/System/Notification/friendtalk_msg.json +0 -91
  25. package/template/entities/System/Notification/sms_log.json +0 -65
  26. package/template/entities/System/Notification/sms_msg.json +0 -82
  27. package/template/entities/System/Notification/sms_verification.json +0 -50
  28. package/template/entities/System/Payment/pg_cancel.json +0 -60
  29. package/template/entities/System/Payment/pg_order.json +0 -115
  30. package/template/entities/System/Payment/pg_webhook_log.json +0 -52
  31. package/template/entities/System/Push/push_log.json +0 -86
  32. package/template/entities/System/Push/push_msg.json +0 -56
  33. package/template/templates/email/auth/2fa_disabled.html +0 -23
  34. package/template/templates/email/auth/2fa_recovery_regenerated.html +0 -31
  35. package/template/templates/email/auth/2fa_setup_complete.html +0 -43
  36. package/template/templates/email/auth/welcome.html +0 -18
  37. package/template/templates/email/order/order_confirmation.html +0 -30
  38. /package/template/configs/{notification/smtp.json → smtp.json} +0 -0
@@ -1,50 +0,0 @@
1
- {
2
- "name": "sms_verification",
3
- "description": "SMS 인증번호 검증 엔티티. 인증번호 SHA-256 해시 저장, 시도 횟수 관리. 사용자 환경에 맞게 fields를 자유롭게 확장할 수 있습니다.",
4
- "license_scope": false,
5
- "hard_delete": true,
6
- "history": false,
7
- "fields": {
8
- "phone": {
9
- "index": true,
10
- "hash": true,
11
- "comment": "수신 전화번호",
12
- "type": "varchar(20)",
13
- "required": true
14
- },
15
- "purpose": {
16
- "index": true,
17
- "comment": "인증 목적",
18
- "type": [
19
- "signup",
20
- "password_reset",
21
- "identity"
22
- ],
23
- "required": true
24
- },
25
- "status": {
26
- "index": true,
27
- "type": [
28
- "pending",
29
- "verified",
30
- "expired"
31
- ],
32
- "default": "pending",
33
- "required": true
34
- },
35
- "code_hash": {
36
- "type": "varchar(64)",
37
- "required": true,
38
- "comment": "인증번호 SHA-256 해시"
39
- },
40
- "expires_at": {
41
- "required": true,
42
- "comment": "만료 시각"
43
- },
44
- "attempts": {
45
- "type": "uint",
46
- "default": 0,
47
- "comment": "검증 시도 횟수"
48
- }
49
- }
50
- }
@@ -1,60 +0,0 @@
1
- {
2
- "name": "pg_cancel",
3
- "description": "PG 결제 취소 이력 엔티티. 부분/전액 취소를 기록합니다. 사용자 환경에 맞게 fields를 자유롭게 확장할 수 있습니다.",
4
- "read_only": true,
5
- "fields": {
6
- "order_seq": {
7
- "index": true,
8
- "type": "integer",
9
- "required": true,
10
- "comment": "pg_order.seq 참조"
11
- },
12
- "order_id": {
13
- "index": true,
14
- "type": "string",
15
- "required": true,
16
- "comment": "주문번호 (조회 편의)"
17
- },
18
- "cancel_amount": {
19
- "index": true,
20
- "type": "integer",
21
- "required": true,
22
- "comment": "취소 금액"
23
- },
24
- "cancel_reason": {
25
- "index": true,
26
- "type": "string",
27
- "required": true,
28
- "comment": "취소 사유 (최대 200자)"
29
- },
30
- "cancel_status": {
31
- "index": true,
32
- "type": [
33
- "done",
34
- "failed"
35
- ],
36
- "default": "done",
37
- "comment": "취소 처리 상태"
38
- },
39
- "transaction_key": {
40
- "index": true,
41
- "type": "string",
42
- "comment": "PG사 취소 거래 키"
43
- },
44
- "canceled_time": {
45
- "comment": "취소 시각 (ISO 8601)"
46
- },
47
- "refundable_amount": {
48
- "comment": "취소 후 환불 가능 잔액"
49
- },
50
- "tax_free_amount": {
51
- "comment": "취소된 금액 중 면세 금액"
52
- },
53
- "receipt_key": {
54
- "comment": "현금영수증 키 (해당 시)"
55
- },
56
- "refund_account": {
57
- "comment": "환불 계좌 정보 JSON (가상계좌 취소 시)"
58
- }
59
- }
60
- }
@@ -1,115 +0,0 @@
1
- {
2
- "name": "pg_order",
3
- "description": "PG 결제 주문 엔티티. 결제 요청/승인/취소 상태를 추적합니다. 사용자 환경에 맞게 fields를 자유롭게 확장할 수 있습니다.",
4
- "fields": {
5
- "order_id": {
6
- "index": true,
7
- "type": "string",
8
- "required": true,
9
- "unique": true,
10
- "comment": "고유 주문번호 (6~64자, [a-zA-Z0-9_-])"
11
- },
12
- "status": {
13
- "index": true,
14
- "type": [
15
- "created",
16
- "ready",
17
- "in_progress",
18
- "waiting",
19
- "done",
20
- "canceled",
21
- "partial_canceled",
22
- "aborted",
23
- "expired"
24
- ],
25
- "default": "created",
26
- "comment": "결제 상태"
27
- },
28
- "payment_key": {
29
- "index": true,
30
- "type": "string",
31
- "comment": "PG사 결제 키 (승인 후 할당)"
32
- },
33
- "provider": {
34
- "index": true,
35
- "type": "string",
36
- "required": true,
37
- "comment": "PG 프로바이더 키 (toss_payments, kcp, inicis 등)"
38
- },
39
- "method": {
40
- "index": true,
41
- "type": "string",
42
- "comment": "결제수단 (card, virtual_account, transfer, phone, easy_pay)"
43
- },
44
- "amount": {
45
- "index": true,
46
- "type": "integer",
47
- "required": true,
48
- "comment": "총 결제 금액 (원)"
49
- },
50
- "balance_amount": {
51
- "index": true,
52
- "type": "integer",
53
- "comment": "취소 가능 잔액"
54
- },
55
- "currency": {
56
- "index": true,
57
- "type": "string",
58
- "default": "KRW",
59
- "comment": "통화 코드 (ISO 4217)"
60
- },
61
- "account_seq": {
62
- "index": true,
63
- "type": "integer",
64
- "comment": "구매자 계정 seq (로그인 사용자)"
65
- },
66
- "customer_name": {
67
- "index": true,
68
- "hash": true,
69
- "type": "string",
70
- "comment": "구매자명"
71
- },
72
- "customer_email": {
73
- "index": true,
74
- "hash": true,
75
- "type": "string",
76
- "comment": "구매자 이메일"
77
- },
78
- "order_name": {
79
- "comment": "주문 상품명 (예: '토스 티셔츠 외 2건')"
80
- },
81
- "requested_time": {
82
- "comment": "결제 요청 시각 (ISO 8601)"
83
- },
84
- "approved_time": {
85
- "comment": "결제 승인 시각 (ISO 8601)"
86
- },
87
- "card_info": {
88
- "comment": "카드 결제 정보 JSON (마스킹된 카드번호, 카드타입 등)"
89
- },
90
- "virtual_account_info": {
91
- "comment": "가상계좌 정보 JSON (계좌번호, 은행, 입금기한)"
92
- },
93
- "easy_pay_info": {
94
- "comment": "간편결제 정보 JSON (provider, amount, discountAmount)"
95
- },
96
- "receipt_url": {
97
- "comment": "영수증 URL"
98
- },
99
- "checkout_url": {
100
- "comment": "결제창 URL"
101
- },
102
- "failure_code": {
103
- "comment": "실패 에러 코드"
104
- },
105
- "failure_message": {
106
- "comment": "실패 에러 메시지"
107
- },
108
- "metadata": {
109
- "comment": "상점 커스텀 데이터 JSON (최대 5개 key-value)"
110
- },
111
- "pg_raw_response": {
112
- "comment": "PG사 원본 응답 JSON (디버깅용)"
113
- }
114
- }
115
- }
@@ -1,52 +0,0 @@
1
- {
2
- "name": "pg_webhook_log",
3
- "description": "PG 웹훅 수신 이력. 모든 수신 이벤트를 기록합니다. 사용자 환경에 맞게 fields를 자유롭게 확장할 수 있습니다.",
4
- "hard_delete": true,
5
- "read_only": true,
6
- "compress": true,
7
- "fields": {
8
- "event_type": {
9
- "index": true,
10
- "type": "string",
11
- "required": true,
12
- "comment": "웹훅 이벤트 타입 (payment.done, payment.canceled 등)"
13
- },
14
- "order_id": {
15
- "index": true,
16
- "type": "string",
17
- "comment": "관련 주문번호"
18
- },
19
- "payment_key": {
20
- "index": true,
21
- "type": "string",
22
- "comment": "관련 결제 키"
23
- },
24
- "status": {
25
- "index": true,
26
- "type": [
27
- "received",
28
- "processed",
29
- "failed"
30
- ],
31
- "default": "received",
32
- "comment": "웹훅 처리 상태"
33
- },
34
- "provider": {
35
- "index": true,
36
- "type": "string",
37
- "comment": "PG 프로바이더 키"
38
- },
39
- "payload": {
40
- "comment": "수신한 웹훅 원본 JSON"
41
- },
42
- "processed_time": {
43
- "comment": "처리 완료 시각"
44
- },
45
- "error_message": {
46
- "comment": "처리 실패 시 에러 메시지"
47
- },
48
- "signature": {
49
- "comment": "웹훅 서명값 (검증용)"
50
- }
51
- }
52
- }
@@ -1,86 +0,0 @@
1
- {
2
- "name": "push_log",
3
- "description": "푸시 알림 발송 이력. 발송 결과 추적 및 재시도 관리용. account_seq(JWT) 또는 device_id(HMAC) 중 하나 존재. 사용자 환경에 맞게 fields를 자유롭게 확장할 수 있습니다.",
4
- "license_scope": false,
5
- "hard_delete": true,
6
- "history_ttl": 30,
7
- "compress": true,
8
- "fields": {
9
- "account_seq": {
10
- "index": true,
11
- "comment": "수신자 account seq (JWT 인증 시. nullable)"
12
- },
13
- "attempt_time": {
14
- "index": true,
15
- "comment": "처리 시작(claim) 시각 — 비정상 종료 감지용"
16
- },
17
- "body": {
18
- "comment": "알림 본문"
19
- },
20
- "device_id": {
21
- "index": true,
22
- "comment": "대상 디바이스 ID (HMAC 인증 시. nullable)",
23
- "type": "varchar(255)"
24
- },
25
- "device_seq": {
26
- "index": true,
27
- "comment": "대상 디바이스 seq (account_device.seq)"
28
- },
29
- "device_token": {
30
- "comment": "발송 시점 디바이스 토큰 스냅샷 (FCM registration token 또는 APNs device token)"
31
- },
32
- "error_message": {
33
- "comment": "실패 시 오류 메시지",
34
- "type": "varchar(500)"
35
- },
36
- "platform": {
37
- "index": true,
38
- "comment": "발송 채널",
39
- "type": [
40
- "fcm",
41
- "apns",
42
- "web"
43
- ]
44
- },
45
- "push_data": {
46
- "comment": "커스텀 페이로드 JSON 문자열",
47
- "type": "varchar(2000)"
48
- },
49
- "ref_entity": {
50
- "index": true,
51
- "comment": "트리거 엔티티명"
52
- },
53
- "ref_seq": {
54
- "index": true,
55
- "comment": "트리거 레코드 seq"
56
- },
57
- "retry_count": {
58
- "index": true,
59
- "comment": "재시도 횟수",
60
- "type": "uint",
61
- "default": 0
62
- },
63
- "sent_time": {
64
- "index": true,
65
- "comment": "발송 완료 시각"
66
- },
67
- "status": {
68
- "index": true,
69
- "comment": "발송 상태",
70
- "type": [
71
- "pending",
72
- "processing",
73
- "sent",
74
- "delivered",
75
- "failed",
76
- "expired"
77
- ],
78
- "default": "pending",
79
- "required": true
80
- },
81
- "title": {
82
- "index": true,
83
- "comment": "알림 제목"
84
- }
85
- }
86
- }
@@ -1,56 +0,0 @@
1
- {
2
- "name": "push_msg",
3
- "description": "시스템 푸시 트리거 엔티티. insert 시 push hook로 비동기 발송. account_seq(JWT) 또는 device_id(HMAC) 중 하나 필수. 사용자 환경에 맞게 fields를 자유롭게 확장할 수 있습니다.",
4
- "fields": {
5
- "account_seq": {
6
- "index": true,
7
- "comment": "수신자 account seq (JWT 인증 시 필수. HMAC 인증은 device_id 사용)",
8
- "required": true
9
- },
10
- "device_id": {
11
- "index": true,
12
- "comment": "대상 디바이스 ID (HMAC 인증 시. 기기단위로 account 없이 푸시 발송)",
13
- "type": "varchar(255)"
14
- },
15
- "title": {
16
- "index": true,
17
- "comment": "푸시 제목",
18
- "required": true
19
- },
20
- "message": {
21
- "comment": "푸시 본문",
22
- "nullable": true
23
- },
24
- "ref_entity": {
25
- "comment": "참조 엔티티명",
26
- "nullable": true
27
- },
28
- "ref_seq": {
29
- "comment": "참조 레코드 seq",
30
- "nullable": true
31
- },
32
- "msg_data": {
33
- "comment": "추가 데이터(JSON 문자열)",
34
- "type": "varchar(2000)",
35
- "nullable": true
36
- }
37
- },
38
- "hooks": {
39
- "after_insert": [
40
- {
41
- "type": "push",
42
- "target_account_seq": "account_seq",
43
- "target_device_id": "device_id",
44
- "comment": "account_seq 있으면 계정 기반 발송, 없으면 device_id 기반 발송",
45
- "title": "${new.title}",
46
- "push_body": "${new.message}",
47
- "push_data": {
48
- "push_msg_seq": "${new.seq}",
49
- "ref_entity": "${new.ref_entity}",
50
- "ref_seq": "${new.ref_seq}",
51
- "msg_data": "${new.msg_data}"
52
- }
53
- }
54
- ]
55
- }
56
- }
@@ -1,23 +0,0 @@
1
- <h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">2단계 인증이 비활성화되었습니다</h2>
2
- <p style="margin: 0 0 16px; font-size: 15px; color: #555; line-height: 1.6;">
3
- <strong>${email}</strong> 계정의 2단계 인증(TOTP)이 해제되었습니다.<br>
4
- 앞으로 이메일과 비밀번호만으로 로그인됩니다.
5
- </p>
6
-
7
- <div style="margin: 0 0 24px; padding: 20px; background-color: #f8d7da; border-radius: 8px; border-left: 4px solid #dc3545;">
8
- <p style="margin: 0; font-size: 14px; color: #721c24; line-height: 1.6;">
9
- <strong>보안 알림:</strong> 2단계 인증이 해제되면 계정 보안 수준이 낮아집니다.<br>
10
- 가능하면 다시 2단계 인증을 설정하는 것을 권장합니다.
11
- </p>
12
- </div>
13
-
14
- <p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
15
- 해제 시각: ${disabled_time}
16
- </p>
17
- <p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
18
- 해제 방법: ${disabled_by|본인 요청}
19
- </p>
20
-
21
- <p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
22
- 본인이 해제하지 않은 경우, 즉시 비밀번호를 변경하고 관리자에게 문의하세요.
23
- </p>
@@ -1,31 +0,0 @@
1
- <h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">복구 코드가 재생성되었습니다</h2>
2
- <p style="margin: 0 0 16px; font-size: 15px; color: #555; line-height: 1.6;">
3
- <strong>${email}</strong> 계정의 2단계 인증 복구 코드가 새로 생성되었습니다.<br>
4
- 이전 복구 코드는 모두 <strong>폐기</strong>되었습니다.
5
- </p>
6
-
7
- <div style="margin: 0 0 24px; padding: 20px; background-color: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107;">
8
- <p style="margin: 0 0 8px; font-size: 14px; font-weight: 700; color: #856404;">⚠ 새 복구 코드를 안전한 곳에 저장하세요</p>
9
- <p style="margin: 0; font-size: 13px; color: #856404; line-height: 1.5;">
10
- 각 코드는 <strong>한 번만</strong> 사용할 수 있으며, 이 이메일 이후에는 다시 확인할 수 없습니다.
11
- </p>
12
- </div>
13
-
14
- <div style="text-align: center; margin: 0 0 24px;">
15
- <table role="presentation" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
16
- <tr>
17
- <td style="padding: 16px 32px; background-color: #f0f0f5; border-radius: 8px;">
18
- <pre style="margin: 0; font-size: 18px; font-weight: 700; color: #1a1a2e; font-family: 'Courier New', monospace; letter-spacing: 2px; line-height: 2;">${recovery_codes}</pre>
19
- </td>
20
- </tr>
21
- </table>
22
- </div>
23
-
24
- <p style="margin: 0 0 16px; font-size: 14px; color: #888; line-height: 1.6;">
25
- 새 복구 코드: <strong>${recovery_count|10}개</strong><br>
26
- 재생성 시각: ${regenerated_time}
27
- </p>
28
-
29
- <p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
30
- 본인이 요청하지 않은 경우, 계정이 보안 위협에 노출되었을 수 있습니다. 즉시 비밀번호를 변경하고 관리자에게 문의하세요.
31
- </p>
@@ -1,43 +0,0 @@
1
- <h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">2단계 인증이 활성화되었습니다</h2>
2
- <p style="margin: 0 0 16px; font-size: 15px; color: #555; line-height: 1.6;">
3
- <strong>${email}</strong> 계정에 2단계 인증(TOTP)이 설정되었습니다.<br>
4
- 앞으로 로그인 시 Authenticator 앱의 인증 코드가 필요합니다.
5
- </p>
6
-
7
- <div style="margin: 0 0 24px; padding: 20px; background-color: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107;">
8
- <p style="margin: 0 0 8px; font-size: 14px; font-weight: 700; color: #856404;">⚠ 복구 코드를 안전한 곳에 저장하세요</p>
9
- <p style="margin: 0 0 12px; font-size: 13px; color: #856404; line-height: 1.5;">
10
- Authenticator 앱에 접근할 수 없는 경우 아래 복구 코드로 로그인할 수 있습니다.<br>
11
- 각 코드는 <strong>한 번만</strong> 사용할 수 있으며, 이 이메일 이후에는 다시 확인할 수 없습니다.
12
- </p>
13
- </div>
14
-
15
- <div style="text-align: center; margin: 0 0 24px;">
16
- <table role="presentation" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
17
- <tr>
18
- <td style="padding: 16px 32px; background-color: #f0f0f5; border-radius: 8px;">
19
- <pre style="margin: 0; font-size: 18px; font-weight: 700; color: #1a1a2e; font-family: 'Courier New', monospace; letter-spacing: 2px; line-height: 2;">${recovery_codes}</pre>
20
- </td>
21
- </tr>
22
- </table>
23
- </div>
24
-
25
- <p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
26
- 남은 복구 코드: <strong>${recovery_count|10}개</strong>
27
- </p>
28
- <p style="margin: 0 0 16px; font-size: 14px; color: #888; line-height: 1.6;">
29
- 설정 시각: ${enabled_time}
30
- </p>
31
-
32
- <div style="margin: 0 0 16px; padding: 16px; background-color: #f8f9fa; border-radius: 6px;">
33
- <p style="margin: 0; font-size: 13px; color: #666; line-height: 1.6;">
34
- 💡 <strong>권장 사항</strong><br>
35
- • 복구 코드를 출력하거나 비밀번호 관리자에 저장하세요<br>
36
- • 복구 코드가 3개 이하로 줄면 재생성하세요<br>
37
- • 본인이 설정하지 않았다면 즉시 비밀번호를 변경하세요
38
- </p>
39
- </div>
40
-
41
- <p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
42
- 본인이 설정하지 않은 경우, 계정이 보안 위협에 노출되었을 수 있습니다. 즉시 비밀번호를 변경하고 관리자에게 문의하세요.
43
- </p>
@@ -1,18 +0,0 @@
1
- <h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">환영합니다, ${name|회원}님!</h2>
2
- <p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
3
- 회원가입이 완료되었습니다. ${app_name|서비스}를 이용해 주셔서 감사합니다.
4
- </p>
5
- <div style="text-align: center; margin: 0 0 24px;">
6
- <a href="${login_url|#}" style="display: inline-block; padding: 14px 36px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 15px; font-weight: 600;">시작하기</a>
7
- </div>
8
- <div style="padding: 20px; background-color: #f8f8fa; border-radius: 6px; margin: 0 0 24px;">
9
- <p style="margin: 0 0 8px; font-size: 14px; font-weight: 600; color: #1a1a2e;">계정 정보</p>
10
- <p style="margin: 0; font-size: 14px; color: #555; line-height: 1.8;">
11
- 이메일: ${email|user@example.com}<br>
12
- 아이디: ${account_id|user001}<br>
13
- 가입일: ${created_at|오늘}
14
- </p>
15
- </div>
16
- <p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
17
- 궁금한 점이 있으시면 언제든 문의해 주세요.
18
- </p>
@@ -1,30 +0,0 @@
1
- <h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">주문이 확인되었습니다</h2>
2
- <p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
3
- ${name|고객}님, 주문해 주셔서 감사합니다.
4
- </p>
5
- <div style="padding: 20px; background-color: #f8f8fa; border-radius: 6px; margin: 0 0 24px;">
6
- <table role="presentation" width="100%" cellpadding="0" cellspacing="0">
7
- <tr>
8
- <td style="padding: 8px 0; font-size: 14px; color: #888;">주문번호</td>
9
- <td style="padding: 8px 0; font-size: 14px; color: #1a1a2e; font-weight: 600; text-align: right;">${order_id|ORD-000001}</td>
10
- </tr>
11
- <tr>
12
- <td style="padding: 8px 0; font-size: 14px; color: #888; border-top: 1px solid #eaeaea;">주문일시</td>
13
- <td style="padding: 8px 0; font-size: 14px; color: #1a1a2e; text-align: right; border-top: 1px solid #eaeaea;">${order_date|2026-01-01}</td>
14
- </tr>
15
- <tr>
16
- <td style="padding: 8px 0; font-size: 14px; color: #888; border-top: 1px solid #eaeaea;">상품</td>
17
- <td style="padding: 8px 0; font-size: 14px; color: #1a1a2e; text-align: right; border-top: 1px solid #eaeaea;">${items|상품 1건}</td>
18
- </tr>
19
- <tr>
20
- <td style="padding: 12px 0; font-size: 16px; font-weight: 700; color: #1a1a2e; border-top: 2px solid #1a1a2e;">총 결제금액</td>
21
- <td style="padding: 12px 0; font-size: 16px; font-weight: 700; color: #1a1a2e; text-align: right; border-top: 2px solid #1a1a2e;">${total|50,000원}</td>
22
- </tr>
23
- </table>
24
- </div>
25
- <div style="text-align: center; margin: 0 0 24px;">
26
- <a href="${detail_url|#}" style="display: inline-block; padding: 14px 36px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 15px; font-weight: 600;">주문 상세 보기</a>
27
- </div>
28
- <p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
29
- 주문 관련 문의사항이 있으시면 고객센터로 연락해 주세요.
30
- </p>