feat: QR 토큰 품목 상세 전송 지원 (items 파라미터)
This commit is contained in:
@@ -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__":
|
||||
# 테스트
|
||||
|
||||
Reference in New Issue
Block a user