Skip to content

Commit 462e221

Browse files
committed
feat: 결제 완료 이후 알림톡 + 이메일 발송하는 celery task 수행하도록 변경 (초안)
1 parent 5e79385 commit 462e221

3 files changed

Lines changed: 116 additions & 1 deletion

File tree

app/core/settings.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,16 @@
456456
refund_authorizer_secret_key=env("REFUND_AUTHORIZER_SECRET_KEY", default="local_refund_authorizer_secret_key"),
457457
)
458458

459+
# Notification Settings
460+
# 각 템플릿 코드는 NHN Cloud Console에 등록된 코드와 일치해야 합니다.
461+
# NHN Cloud → DB 동기화 후 해당 code로 템플릿을 조회합니다.
462+
NOTIFICATION = types.SimpleNamespace(
463+
# TODO: NHN Cloud에 등록된 결제 완료 알림톡 템플릿 코드로 교체
464+
payment_completed_alimtalk_template_code=env("PAYMENT_COMPLETED_ALIMTALK_TEMPLATE_CODE", default=""),
465+
# TODO: 결제 완료 이메일 템플릿 생성 후 해당 코드로 교체
466+
payment_completed_email_template_code=env("PAYMENT_COMPLETED_EMAIL_TEMPLATE_CODE", default=""),
467+
)
468+
459469
# External API Key Settings (등록 데스크 등)
460470
EXT_API_KEYS = {
461471
"registration_desk": env("API_KEY_REGISTRATION_DESK", default=None),

app/shop/payment_history/serializers.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,20 @@ def create(self, validated_data: dict) -> PaymentHistory:
143143
product_rel.status = OrderProductRelation.OrderProductStatus.paid
144144
product_rel.save()
145145

146-
return PaymentHistory.objects.create(
146+
payment_history = PaymentHistory.objects.create(
147147
order=order,
148148
imp_id=validated_data["imp_uid"],
149149
status=next_status,
150150
price=payment_info["amount"],
151151
)
152152

153+
# 결제 완료 알림(알림톡 + 이메일)을 트랜잭션 커밋 후 비동기로 발송.
154+
from shop.payment_history.tasks import send_payment_completed_notifications
155+
156+
transaction.on_commit(lambda: send_payment_completed_notifications.delay(str(order.id)))
157+
158+
return payment_history
159+
153160
@staticmethod
154161
def _lock_or_promote_order(obj_id: str) -> Order:
155162
"""Order 가 있으면 lock 하여 반환. SingleProductCart 만 있으면 lock + to_order() 로 승격.

app/shop/payment_history/tasks.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import logging
2+
3+
from celery import shared_task
4+
5+
slack_logger = logging.getLogger("slack_logger")
6+
logger = logging.getLogger(__name__)
7+
8+
9+
@shared_task(ignore_result=True)
10+
def send_payment_completed_notifications(order_id: str) -> None:
11+
"""결제 완료 시 알림톡 + 이메일 자동 발송 (비동기 Celery task).
12+
13+
- customer_info가 없는 주문은 발송을 건너뛰고 slack_logger로 누락 사실을 기록합니다.
14+
- 알림톡과 이메일은 독립적으로 시도되며, 한 채널 실패가 다른 채널 발송을 막지 않습니다.
15+
- 실제 외부 API 호출은 send_notification_to_recipient task에서 채널별로 처리됩니다.
16+
"""
17+
from django.conf import settings
18+
from shop.order.models import Order
19+
20+
order = Order.objects.filter_active().select_related("customer_info").filter(id=order_id).first()
21+
22+
if order is None:
23+
slack_logger.error("결제 완료 알림 발송 실패: 주문을 찾을 수 없습니다. order_id=%s", order_id)
24+
return
25+
26+
customer_info = getattr(order, "customer_info", None)
27+
if customer_info is None:
28+
slack_logger.error(
29+
"결제 완료 알림 발송 누락: customer_info가 없는 주문입니다. order_id=%s",
30+
order_id,
31+
)
32+
return
33+
34+
# TODO: 알림톡/이메일 템플릿 변수 확정 후 context 업데이트 필요.
35+
# 템플릿에서 사용하는 #{변수명} / {{ 변수명 }} 목록에 맞게 값을 추가하세요.
36+
context = {
37+
"phone": customer_info.phone,
38+
"email": customer_info.email,
39+
}
40+
41+
_send_alimtalk(order_id, customer_info.phone, context, settings)
42+
_send_email(order_id, customer_info.email, context, settings)
43+
44+
45+
def _send_alimtalk(order_id: str, recipient_phone: str, context: dict, settings) -> None:
46+
from notification.models import (
47+
NHNCloudKakaoAlimTalkNotificationHistory,
48+
NHNCloudKakaoAlimTalkNotificationTemplate,
49+
)
50+
51+
try:
52+
template_code = settings.NOTIFICATION.payment_completed_alimtalk_template_code
53+
template = NHNCloudKakaoAlimTalkNotificationTemplate.objects.filter_active().filter(code=template_code).first()
54+
if template is None:
55+
slack_logger.error(
56+
"결제 완료 알림톡 발송 실패: 템플릿을 찾을 수 없습니다. template_code=%s order_id=%s",
57+
template_code,
58+
order_id,
59+
)
60+
return
61+
62+
history = NHNCloudKakaoAlimTalkNotificationHistory.objects.create_for_recipients(
63+
template=template,
64+
recipients=[{"recipient": recipient_phone, "context": context}],
65+
)
66+
history.send()
67+
except Exception:
68+
slack_logger.exception(
69+
"결제 완료 알림톡 발송 중 예외 발생. order_id=%s",
70+
order_id,
71+
)
72+
73+
74+
def _send_email(order_id: str, recipient_email: str, context: dict, settings) -> None:
75+
from notification.models import EmailNotificationHistory, EmailNotificationTemplate
76+
77+
try:
78+
template_code = settings.NOTIFICATION.payment_completed_email_template_code
79+
template = EmailNotificationTemplate.objects.filter_active().filter(code=template_code).first()
80+
if template is None:
81+
# 이메일 템플릿은 아직 미생성 상태일 수 있으므로 warning 수준으로 기록.
82+
logger.warning(
83+
"결제 완료 이메일 발송 건너뜀: 템플릿을 찾을 수 없습니다. template_code=%s order_id=%s",
84+
template_code,
85+
order_id,
86+
)
87+
return
88+
89+
history = EmailNotificationHistory.objects.create_for_recipients(
90+
template=template,
91+
recipients=[{"recipient": recipient_email, "context": context}],
92+
)
93+
history.send()
94+
except Exception:
95+
slack_logger.exception(
96+
"결제 완료 이메일 발송 중 예외 발생. order_id=%s",
97+
order_id,
98+
)

0 commit comments

Comments
 (0)