feat: QR 토큰 품목 상세 전송 지원 (items 파라미터)

This commit is contained in:
thug0bin
2026-03-29 12:37:36 +09:00
parent 3871154509
commit 21e1c3adfa
13 changed files with 3955 additions and 52 deletions

View File

@@ -1,10 +1,14 @@
"""
QR Claim Token 생성 모듈
후향적 적립을 위한 1회성 토큰 생성
v2 (2026-03-29): 서버 즉시 전송 추가 (pos.pharmq.kr)
"""
import hashlib
import secrets
import logging
import requests
from datetime import datetime, timedelta
import sys
import os
@@ -16,7 +20,14 @@ from db.dbsetup import DatabaseManager
# 설정값
MILEAGE_RATE = 0.03 # 3% 적립
TOKEN_EXPIRY_DAYS = 30 # 30일 유효기간
QR_BASE_URL = "https://mile.0bin.in/claim"
# 서버 설정 (v2)
CLOUD_API_URL = "https://pos.pharmq.kr"
PHARMACY_CODE = "P0001"
QR_BASE_URL = f"{CLOUD_API_URL}/{PHARMACY_CODE}/claim"
# 로거
logger = logging.getLogger(__name__)
def generate_claim_token(transaction_id, total_amount, pharmacy_id="YANGGU001"):
@@ -77,7 +88,8 @@ def generate_claim_token(transaction_id, total_amount, pharmacy_id="YANGGU001"):
'expires_at': expires_at,
'pharmacy_id': pharmacy_id,
'transaction_id': transaction_id,
'total_amount': total_amount
'total_amount': total_amount,
'nonce': nonce, # 서버 전송용
}
@@ -150,6 +162,97 @@ def save_token_to_db(transaction_id, token_hash, total_amount, claimable_points,
return (False, f"DB 저장 실패: {str(e)}")
def sync_token_to_server(transaction_id, total_amount, pharmacy_code=None, items=None):
"""
토큰을 서버(pos.pharmq.kr)에 즉시 전송 (품목 상세 포함)
Args:
transaction_id: 거래 ID
total_amount: 판매 금액
pharmacy_code: 약국 코드 (기본값: P0001)
items: 품목 리스트 [{'item_code': ..., 'item_name': ..., 'quantity': ..., 'unit_price': ..., 'total_price': ...}]
Returns:
tuple: (성공 여부, 서버 응답 or 에러 메시지)
"""
pharmacy_code = pharmacy_code or PHARMACY_CODE
payload = {
'pharmacy_code': pharmacy_code,
'transaction_id': str(transaction_id),
'total_amount': int(total_amount),
}
# 품목 상세 추가
if items:
payload['items'] = items
try:
response = requests.post(
f"{CLOUD_API_URL}/api/v1/tokens/create",
json=payload,
timeout=5
)
if response.ok:
result = response.json()
logger.info(f"[QR] 서버 전송 성공: {transaction_id}{result.get('points', 0)}P")
return (True, result)
else:
logger.warning(f"[QR] 서버 응답 오류: {response.status_code} - {response.text[:100]}")
return (False, f"서버 오류: {response.status_code}")
except requests.Timeout:
logger.warning(f"[QR] 서버 타임아웃 (오프라인?): {transaction_id}")
return (False, "타임아웃")
except Exception as e:
logger.warning(f"[QR] 서버 전송 실패: {e}")
return (False, str(e))
def generate_and_sync_token(transaction_id, total_amount, pharmacy_id="P0001", items=None):
"""
토큰 생성 + 로컬 저장 + 서버 즉시 전송 (통합 함수)
Args:
transaction_id: 거래 ID
total_amount: 판매 금액
pharmacy_id: 약국 코드
items: 품목 리스트 [{'item_code': ..., 'item_name': ..., 'quantity': ..., 'unit_price': ..., 'total_price': ...}]
Returns:
dict: 토큰 정보 + synced 플래그
"""
# 1. 토큰 생성
token_info = generate_claim_token(transaction_id, total_amount, pharmacy_id)
# 2. 로컬 DB 저장
local_success, local_error = save_token_to_db(
transaction_id,
token_info['token_hash'],
total_amount,
token_info['claimable_points'],
token_info['expires_at'],
pharmacy_id
)
token_info['local_saved'] = local_success
if not local_success:
token_info['local_error'] = local_error
# 3. ⚡ 서버 즉시 전송 (품목 포함)
sync_success, sync_result = sync_token_to_server(
transaction_id, total_amount, pharmacy_id, items=items
)
token_info['synced'] = sync_success
if sync_success and isinstance(sync_result, dict):
token_info['server_token_id'] = sync_result.get('token_id')
token_info['server_points'] = sync_result.get('points')
return token_info
# 테스트 코드
if __name__ == "__main__":
# 테스트