package com.bb.front;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HashMap;

import jakarta.servlet.http.HttpServletRequest;

import org.apache.http.conn.ConnectTimeoutException;
import org.codehaus.jettison.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

import com.bb.model.BetRadarResponse;
import com.bb.service.AsyncSiteService;
import com.bb.service.CallBackService;
import com.bb.service.SiteService;
import com.bb.util.HmacUtils;

import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/api/callback/betradar")
@RequiredArgsConstructor
@SecurityRequirement(name = "Authorization")
@EnableScheduling
@RestController
public class CallbackBetRadarController {
	
	@Autowired
	SiteService siteService;
	
	@Autowired
	CallBackService callBackService;
	
	@Autowired
	AsyncSiteService asyncCallBackService;
	
	@ResponseBody
	@RequestMapping(path = "/balance")
	public ResponseEntity<BetRadarResponse> balance(
	        HttpServletRequest request,
	        @RequestHeader("timestamp") String timestamp,
	        @RequestHeader("signature") String signature,
	        @RequestBody net.sf.json.JSONObject requestBody) throws Exception {
		BetRadarResponse response = null;
		
		String username = requestBody.getString("username");
		String target = "";
		int idx = username.indexOf('_');
		if (idx >= 0 && idx < username.length() - 1) {
		    target = username.substring(idx + 1);
		} else {
			target = username;
		}
		long siteIdx = Long.parseLong(target.substring(0, 3), 16);
		String memberId = target.substring(3, target.length());
		final String LOG_PREFIX = "#-CB::BET_RADAR::"+memberId+"::balance:::";
		log.info(LOG_PREFIX+ "Request::" + requestBody);
		
		try {
			//사이트정보
			HashMap sParam = new HashMap();
			sParam.put("siteIdx", siteIdx);
			sParam.put("memberId",  memberId);
			sParam.put("vendor", "bet_radar");
			HashMap siteApiInfo = siteService.getSiteApiInfo2(sParam);
			if(siteApiInfo == null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_USER");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			/*
			// ===== Signature 검증 (여기서!) =====
	        String apiKey = siteApiInfo.get("vendorApiKey").toString();
	        String rawPayload = getRawBody(request);

	        if (!verifySignature(timestamp, rawPayload, signature, apiKey)) {
	            log.error(LOG_PREFIX + "INVALID_SIGNATURE");

	            response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<>(response, HttpStatus.OK);
	        }
	        // ==================================
			*/
			
			// ===== Signature 검증 (관찰 모드) =====
			try {
			    String apiKey = siteApiInfo.get("vendorApiKey").toString();
			    String rawPayload = getRawBody(request);

			    boolean valid = verifySignature(timestamp, rawPayload, signature, apiKey);

			    if (!valid) {
			        log.error(LOG_PREFIX + "SIGNATURE_MISMATCH");
			        log.error(LOG_PREFIX + "timestamp=" + timestamp);
			        log.error(LOG_PREFIX + "payload(raw)=" + rawPayload);
			        log.error(LOG_PREFIX + "signature(received)=" + signature);

			        String calculated = HmacUtils.hmacSha256Hex(apiKey, timestamp + rawPayload);
			        log.error(LOG_PREFIX + "signature(calculated)=" + calculated);
			    } else {
			        log.info(LOG_PREFIX + "SIGNATURE_OK");
			    }

			} catch (Exception e) {
			    log.error(LOG_PREFIX + "SIGNATURE_EXCEPTION", e);
			}
			// ======================================
			
			log.info(LOG_PREFIX + "PLAY_SEAMLESS");
			org.codehaus.jettison.json.JSONObject member = new org.codehaus.jettison.json.JSONObject();
			member.put("userId", memberId);
			log.info(LOG_PREFIX+ "Callback URL::" + siteApiInfo.get("siteCbUrl")+"/balance");
			log.info(LOG_PREFIX+ "Callback Param::" + member.toString());
		
			member.put("userId", memberId);			
			org.codehaus.jettison.json.JSONObject resBalancedData = callBackService.getBalance(siteApiInfo, member);
			if(resBalancedData == null) {
				log.error(LOG_PREFIX+ "Error Msg: BALANCE_CALLBACK_ERROR");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			int balance = resBalancedData.getInt("balance");
			response = new BetRadarResponse();
            response.setBalance(balance);
            response.setError(0);
			
		} catch (ResourceAccessException rae) {
			if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX+ "[ConnectTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX+ "[SocketTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX+ "[InterruptedException]"+rae.getMessage());
            }
            response = new BetRadarResponse();
            response.setBalance(0);
            response.setError(1);
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "[Exception]"+e.toString());
			response = new BetRadarResponse();
			response.setBalance(0);
            response.setError(1);
		}
		
		ResponseEntity<BetRadarResponse> res = new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
		log.info(LOG_PREFIX+ "Response::" + response.toString());
		
		return res;
	}
	
	@ResponseBody
	@RequestMapping(path = "/bet")
	private ResponseEntity<BetRadarResponse> bet(HttpServletRequest request, @RequestBody net.sf.json.JSONObject requestBody) {
		BetRadarResponse response = null;
		
		String username = requestBody.getString("username");
		String target = "";
		int idx = username.indexOf('_');
		if (idx >= 0 && idx < username.length() - 1) {
		    target = username.substring(idx + 1);
		} else {
			target = username;
		}
		net.sf.json.JSONObject transactionObj = requestBody.getJSONObject("transaction");
		net.sf.json.JSONObject gameObj = transactionObj.getJSONObject("details").getJSONObject("game");
		net.sf.json.JSONArray betInfoArr = transactionObj.getJSONObject("details").getJSONArray("bet_info");
		String type = transactionObj.getString("type");
		String betId = gameObj.getString("id");
		String tranId = betId+"#"+type;
		String vendorTranKey = tranId;
		String betTitle = type + " | " + gameObj.getString("type") + " | " + gameObj.getString("odds");
		long siteIdx = Long.parseLong(target.substring(0, 3), 16);
		String memberId = target.substring(3, target.length());
		final String LOG_PREFIX = "#-CB::BET_RADAR::"+memberId+"::bet::"+betId+":::";
		log.info(LOG_PREFIX+ "Request::" + requestBody);
		
		try {
			// "date":"2025-05-21T08:05:32.925Z" => "2025-05-21 08:05:32"
			String betDateTimeStr = transactionObj.getString("processed_at");
			log.info(LOG_PREFIX+ "========== BF_betDateTimeStr::" + betDateTimeStr);
			betDateTimeStr = betDateTimeStr.substring(0, 19);
			betDateTimeStr = betDateTimeStr.replace("T", " ");
			log.info(LOG_PREFIX+ "========== AF_betDateTimeStr::" + betDateTimeStr);
			
			//사이트정보
			HashMap sParam = new HashMap();
			sParam.put("siteIdx", siteIdx);
			sParam.put("memberId", memberId);
			sParam.put("vendor", "bet_radar");
			sParam.put("vendorGameCode", "SKSPORTS");
			log.info(LOG_PREFIX + "getSiteVendorInfo::"+sParam.toString());
			HashMap<String,String> siteVendorInfo = siteService.getSiteVendorInfo(sParam);
			if(siteVendorInfo == null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_SITE_INFO");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			if(siteVendorInfo.get("memberIdx") == null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_USER");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			String siteId = siteVendorInfo.get("siteId").toString();
			long memberIdx = Long.parseLong(siteVendorInfo.get("memberIdx").toString());
			String vendorApiId = siteVendorInfo.get("vendorApiId").toString();
			String vendorKey = siteVendorInfo.get("vendorKey").toString();
			int vendorIdx = Integer.parseInt(siteVendorInfo.get("vendorIdx").toString());
			String vendorCetegory = siteVendorInfo.get("vendorCetegory").toString();
			int amount = requestBody.getInt("amount");
			
			if(siteVendorInfo.get("lastInVendorIdx")==null || "0".equals(siteVendorInfo.get("lastInVendorIdx").toString())) {
				HashMap memParam = new HashMap<>();
				memParam.put("siteIdx", siteIdx);
				memParam.put("memberIdx", memberIdx);
				memParam.put("lastInVendorIdx", Integer.parseInt(siteVendorInfo.get("vendorIdx").toString()));
				int updResult = siteService.updMemLastVendorIdx(memParam);
			} else {
				int lastInVendorIdx = Integer.parseInt(siteVendorInfo.get("lastInVendorIdx").toString());
				if(lastInVendorIdx != vendorIdx) {
					log.error(LOG_PREFIX+ "VendorIdx doesn't match");
					response = new BetRadarResponse();
		            response.setBalance(0);
		            response.setError(1);
		            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
				}
			}
			
			long siteCredit = Long.parseLong(siteVendorInfo.get("credit").toString());
			if(siteCredit < amount) {
				log.error(LOG_PREFIX+ "NO_SITE_CREDIT");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			long siteMaxBet = Long.parseLong(siteVendorInfo.get("siteMaxBet").toString());
			if(siteMaxBet < amount) {
				log.error(LOG_PREFIX+ "MAX_BET_AMOUNT_OVER");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			HashMap tranParam = new HashMap();
			tranParam.put("tranId", tranId);
			tranParam.put("refId",  betId);
			tranParam.put("siteIdx", siteIdx);
			tranParam.put("siteId", siteId);
			tranParam.put("memberIdx", memberIdx);
			tranParam.put("memberId", memberId);
			tranParam.put("vendorCetegory", vendorCetegory);
			tranParam.put("vendorIdx", vendorIdx);
			tranParam.put("vendorCode", "bet_radar");
			tranParam.put("vendorTranKey", vendorTranKey);
			tranParam.put("gameType", "SKSPORTS");
			tranParam.put("gameId", "");
			tranParam.put("gameIdx", betTitle);
			tranParam.put("tranType", "debit");
			tranParam.put("depositAmt", amount);
			tranParam.put("creditAmt", "0");
			tranParam.put("isCancel", "N");
			tranParam.put("isTie", "N");
			tranParam.put("apiStatus", 0);
			tranParam.put("vendorApiId", vendorApiId);
			log.info(LOG_PREFIX + "-----INSERT_BET_START-----");
			siteService.commonBetinsert(tranParam);
			log.info(LOG_PREFIX + "-----INSERT_BET_END-----");
			
			JSONObject callBackObj = new JSONObject();
			callBackObj.put("betId", betId);
			callBackObj.put("tranId", tranId);
			callBackObj.put("userId", memberId);
			callBackObj.put("vendorIdx", vendorIdx);
			callBackObj.put("vendorKey", vendorKey);
			callBackObj.put("vendor", "bet_radar");
			callBackObj.put("gameIdx", vendorIdx);
			callBackObj.put("gameKey", betTitle);
			callBackObj.put("gameId", "");
			callBackObj.put("gameType", "SKSPORTS");
			callBackObj.put("tranType", "debit");
			callBackObj.put("debit", amount);
			callBackObj.put("credit", 0);
			callBackObj.put("isCancel", 0);
			callBackObj.put("isBonus", 0);
			callBackObj.put("isData", "N");
			callBackObj.put("requestAt", betDateTimeStr);
			String detailJsonStr = transactionObj.getJSONObject("details").toString();
			JSONObject detailObj = new JSONObject(detailJsonStr);
			callBackObj.put("detail", detailObj);
			log.info(LOG_PREFIX + "SEND_BET request body: " + callBackObj.toString());
			JSONObject resData = callBackService.changeBalance(LOG_PREFIX, siteVendorInfo, callBackObj);
			log.info(LOG_PREFIX + "SEND_BET status code: " + resData.getLong("result_code"));
			log.info(LOG_PREFIX + "SEND_BET response body: " + resData.toString());
			
			if(resData.getLong("result_code") == 0) {
				tranParam.put("balance", resData.getInt("balance"));
				int res = siteService.updateCbApi(tranParam);
				log.info(LOG_PREFIX + "UPDATE_CALLBACK_API_STATUS END");
				
				asyncCallBackService.asyncSportsRadarProc(LOG_PREFIX, tranParam, requestBody);
				
				int balance = resData.getInt("balance");
				response = new BetRadarResponse();
	            response.setBalance(balance);
	            response.setError(0);
			} else if(resData.getLong("result_code") == 98) {
				log.error(LOG_PREFIX + "중복요청");
				int balance = resData.getInt("balance");
				response = new BetRadarResponse();
	            response.setBalance(balance);
	            response.setError(0);
			} else if(resData.getLong("result_code") == 80) {
				log.error(LOG_PREFIX + "잔액부족");
				int balance = resData.getInt("balance");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
			} else {
				String msg = "";
				if(resData.has("error_msg")) msg = resData.getString("error_msg");
 				log.error(LOG_PREFIX+ msg);
 				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
			}
			
		} catch (ResourceAccessException rae) {
			if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX+ "[ConnectTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX+ "[SocketTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX+ "[InterruptedException]"+rae.getMessage());
            }
            response = new BetRadarResponse();
            response.setBalance(0);
            response.setError(1);
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "[Exception]"+e.toString());
			response = new BetRadarResponse();
			response.setBalance(0);
            response.setError(1);
		}
		
		ResponseEntity<BetRadarResponse> res = new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
		log.info(LOG_PREFIX+ "Response::" + response.toString());
		
		return res;
	}
	
	/**
	 * 
	 * @param request
	 * @param requestBody
	 * 
	 * > requestBody.transaction.type
	 * - bet 베팅
	 * - win 당첨
	 * - lose 낙첨
	 * - push 적특
	 * - cancle 취소
	 * 
	 * > requestBody.transaction.rollback
	 * - 0 : 정상 처리
	 * - 1 : 롤백 처리
	 * @return
	 */
	@ResponseBody
	@RequestMapping(path = "/result")
	private ResponseEntity<BetRadarResponse> result(HttpServletRequest request, @RequestBody net.sf.json.JSONObject requestBody) {
		BetRadarResponse response = null;
		
		String username = requestBody.getString("username");
		String target = "";
		int idx = username.indexOf('_');
		if (idx >= 0 && idx < username.length() - 1) {
		    target = username.substring(idx + 1);
		} else {
			target = username;
		}
		net.sf.json.JSONObject transactionObj = requestBody.getJSONObject("transaction");
		net.sf.json.JSONObject gameObj = transactionObj.getJSONObject("details").getJSONObject("game");
		net.sf.json.JSONArray betResultInfoArr = transactionObj.getJSONObject("details").getJSONArray("bet_result_info");
		String type = transactionObj.getString("type");
		int rollback = transactionObj.getInt("rollback");	// rollback=1 이면 롤백. 이전 상태와 type이변경
		String betId = gameObj.getString("id");
		String tranId = betId+"#"+type;
		String vendorTranKey = tranId;
		String betTitle = type + " | " + gameObj.getString("type");
		long siteIdx = Long.parseLong(target.substring(0, 3), 16);
		String memberId = target.substring(3, target.length());
		final String LOG_PREFIX = "#-CB::BET_RADAR::"+memberId+"::"+type+"::"+betId+":::";
		log.info(LOG_PREFIX+ "Request::" + requestBody);
		
		try {
			// "date":"2025-05-21T08:05:32.925Z" => "2025-05-21 08:05:32"
			String betDateTimeStr = transactionObj.getString("processed_at");
			log.info(LOG_PREFIX+ "========== BF_betDateTimeStr::" + betDateTimeStr);
			betDateTimeStr = betDateTimeStr.substring(0, 19);
			betDateTimeStr = betDateTimeStr.replace("T", " ");
			log.info(LOG_PREFIX+ "========== AF_betDateTimeStr::" + betDateTimeStr);
			
			//사이트정보
			HashMap sParam = new HashMap();
			sParam.put("siteIdx", siteIdx);
			sParam.put("memberId", memberId);
			sParam.put("vendor", "bet_radar");
			sParam.put("vendorGameCode", "SKSPORTS");
			log.info(LOG_PREFIX + "getSiteVendorInfo::"+sParam.toString());
			HashMap<String,String> siteVendorInfo = siteService.getSiteVendorInfo(sParam);
			if(siteVendorInfo == null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_SITE_INFO");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			if(siteVendorInfo.get("memberIdx") == null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_USER");
				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
	            return new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
			}
			
			String siteId = siteVendorInfo.get("siteId").toString();
			long memberIdx = Long.parseLong(siteVendorInfo.get("memberIdx").toString());
			String vendorApiId = siteVendorInfo.get("vendorApiId").toString();
			String vendorKey = siteVendorInfo.get("vendorKey").toString();
			int vendorIdx = Integer.parseInt(siteVendorInfo.get("vendorIdx").toString());
			String vendorCetegory = siteVendorInfo.get("vendorCetegory").toString();
			int amount = requestBody.getInt("amount");
			
			HashMap tranParam = new HashMap();
			JSONObject callBackObj = new JSONObject();
			if(rollback == 1) {
				// 이전 결과 콜백 조회 및 롤백 처리
				tranId = tranId + "#rb";
				tranParam.put("tranId", tranId);
				tranParam.put("tranId", tranId);
				tranParam.put("refId",  betId);
				tranParam.put("siteIdx", siteIdx);
				tranParam.put("siteId", siteId);
				tranParam.put("memberIdx", memberIdx);
				tranParam.put("memberId", memberId);
				tranParam.put("vendorCetegory", vendorCetegory);
				tranParam.put("vendorIdx", vendorIdx);
				tranParam.put("vendorCode", "bet_radar");
				tranParam.put("vendorTranKey", vendorTranKey);
				tranParam.put("gameType", "SKSPORTS");
				tranParam.put("gameId", "");
				tranParam.put("gameIdx", "#Rollback-"+betTitle);
				if(amount > 0) {
					// 롤백 - 유저 잔액 증가
					tranParam.put("tranType", "credit");
					tranParam.put("depositAmt", "0");
					tranParam.put("creditAmt", Math.abs(amount));
				} else {
					// 롤백 - 유저 잔액 감소
					tranParam.put("tranType", "debit");
					tranParam.put("depositAmt", Math.abs(amount));
					tranParam.put("creditAmt", "0");
				}
				tranParam.put("isCancel", "N");
				tranParam.put("isTie", "N");
				tranParam.put("apiStatus", 0);
				tranParam.put("vendorApiId", vendorApiId);
				log.info(LOG_PREFIX + "-----INSERT_RESULT_START-----");
				siteService.commonBetinsert(tranParam);
				log.info(LOG_PREFIX + "-----INSERT_RESULT_END-----");
				
				callBackObj.put("betId", betId);
				callBackObj.put("tranId", tranId);
				callBackObj.put("userId", memberId);
				callBackObj.put("vendorIdx", vendorIdx);
				callBackObj.put("vendorKey", vendorKey);
				callBackObj.put("vendor", "bet_radar");
				callBackObj.put("gameIdx", vendorIdx);
				callBackObj.put("gameKey", "#Rollback-"+betTitle);
				callBackObj.put("gameId", "");
				callBackObj.put("gameType", "SKSPORTS");
				callBackObj.put("tranType", "adjust");
				if(amount > 0) {
					// 롤백 - 유저 잔액 증가
					callBackObj.put("debit", 0);
					callBackObj.put("credit", Math.abs(amount));
				} else {
					// 롤백 - 유저 잔액 감소
					callBackObj.put("debit", Math.abs(amount));
					callBackObj.put("credit", 0);
				}
				callBackObj.put("isCancel", 0);
				callBackObj.put("isBonus", 0);
				callBackObj.put("isData", "N");
				callBackObj.put("requestAt", betDateTimeStr);
			} else {
				// rollback == 0 > 기본 결과 콜백
				tranParam.put("tranId", tranId);
				tranParam.put("refId",  betId);
				tranParam.put("siteIdx", siteIdx);
				tranParam.put("siteId", siteId);
				tranParam.put("memberIdx", memberIdx);
				tranParam.put("memberId", memberId);
				tranParam.put("vendorCetegory", vendorCetegory);
				tranParam.put("vendorIdx", vendorIdx);
				tranParam.put("vendorCode", "bet_radar");
				tranParam.put("vendorTranKey", vendorTranKey);
				tranParam.put("gameType", "SKSPORTS");
				tranParam.put("gameId", "");
				tranParam.put("gameIdx", betTitle);
				tranParam.put("tranType", "credit");
				tranParam.put("depositAmt", "0");
				tranParam.put("creditAmt", amount);
				if("cancel".equals(type)) {
					tranParam.put("isCancel", "Y");
				} else {
					tranParam.put("isCancel", "N");
				}
				tranParam.put("isTie", "N");
				tranParam.put("apiStatus", 0);
				tranParam.put("vendorApiId", vendorApiId);
				log.info(LOG_PREFIX + "-----INSERT_RESULT_START-----");
				siteService.commonBetinsert(tranParam);
				log.info(LOG_PREFIX + "-----INSERT_RESULT_END-----");
				
				callBackObj.put("betId", betId);
				callBackObj.put("tranId", tranId);
				callBackObj.put("userId", memberId);
				callBackObj.put("vendorIdx", vendorIdx);
				callBackObj.put("vendorKey", vendorKey);
				callBackObj.put("vendor", "bet_radar");
				callBackObj.put("gameIdx", vendorIdx);
				callBackObj.put("gameKey", betTitle);
				callBackObj.put("gameId", "");
				callBackObj.put("gameType", "SKSPORTS");
				callBackObj.put("tranType", "credit");
				callBackObj.put("debit", 0);
				callBackObj.put("credit", amount);
				if("cancel".equals(type)) {
					callBackObj.put("isCancel", 1);
				} else {
					callBackObj.put("isCancel", 0);
				}
				callBackObj.put("isBonus", 0);
				callBackObj.put("isData", "N");
				callBackObj.put("requestAt", betDateTimeStr);
			}
			
			String detailJsonStr = transactionObj.getJSONObject("details").toString();
			JSONObject detailObj = new JSONObject(detailJsonStr);
			callBackObj.put("detail", detailObj);
			
			log.info(LOG_PREFIX + "SEND_RESULT request body: " + callBackObj.toString());
			JSONObject resData = callBackService.changeBalance(LOG_PREFIX, siteVendorInfo, callBackObj);
			log.info(LOG_PREFIX + "SEND_RESULT status code: " + resData.getLong("result_code"));
			log.info(LOG_PREFIX + "SEND_RESULT response body: " + resData.toString());
			
			if(resData.getLong("result_code") == 0) {
				tranParam.put("balance", resData.getInt("balance"));
				int res = siteService.updateCbApi(tranParam);
				log.info(LOG_PREFIX + "UPDATE_CALLBACK_API_STATUS END");
				
				asyncCallBackService.asyncSportsRadarProc(LOG_PREFIX, tranParam, requestBody);
				
				int balance = resData.getInt("balance");
				response = new BetRadarResponse();
	            response.setBalance(balance);
	            response.setError(0);
			} else if(resData.getLong("result_code") == 98) {
				log.error(LOG_PREFIX + "중복요청");
				int balance = resData.getInt("balance");
				response = new BetRadarResponse();
	            response.setBalance(balance);
	            response.setError(0);
			} else {
				String msg = "";
				if(resData.has("error_msg")) msg = resData.getString("error_msg");
 				log.error(LOG_PREFIX+ msg);
 				response = new BetRadarResponse();
	            response.setBalance(0);
	            response.setError(1);
			}
			
		} catch (ResourceAccessException rae) {
			if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX+ "[ConnectTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX+ "[SocketTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX+ "[InterruptedException]"+rae.getMessage());
            }
            response = new BetRadarResponse();
            response.setBalance(0);
            response.setError(1);
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "[Exception]"+e.toString());
			response = new BetRadarResponse();
			response.setBalance(0);
            response.setError(1);
		}
		
		ResponseEntity<BetRadarResponse> res = new ResponseEntity<BetRadarResponse>(response, HttpStatus.OK);
		return res;
	}
	
	private boolean verifySignature(String timestamp, String payload, String receivedSignature, String apiKey) {

		String message = timestamp + payload;
		String calculated = HmacUtils.hmacSha256Hex(apiKey, message);
		
		return MessageDigest.isEqual(
								calculated.getBytes(StandardCharsets.UTF_8),
								receivedSignature.getBytes(StandardCharsets.UTF_8)
								);
	}
	
	private String getRawBody(HttpServletRequest request) throws IOException {
	
		ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
		
		if (wrapper == null) {
			throw new IllegalStateException("Request not wrapped");
		}
		
		byte[] buf = wrapper.getContentAsByteArray();
		return new String(buf, StandardCharsets.UTF_8);
	}

	
}
