using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cosmos.UserFrame; using Cosmos.ServiceProvider; using Cosmos.Common; using Cosmos.CommonManager; using System.Data; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net; using System.Net.Cache; using System.IO; using System.Collections.Specialized; using System.Management; using System.Security.Cryptography; //************************************************************************************************ // 최초 작성 : 2018.02.22, 미주 펀치 APP 와 해피포인트 기능 ; girak.kim // Class : Model > PunchhPosClient(PunchhClientResponse) <--> PunchhServiceCaller (PunchhAPIResponse) // 변경 이력 : //------------------------------------------------------------------------------------------------ // - 수정 이유 : // - 수정 내용 : // - 수정 적용된 내용 : // - 기 타 : //************************************************************************************************ namespace Cosmos.Service { //##############################[결제, 포인트 관련 인터페이스 구현 클래스]#############################################// class PunchhHappyPoint_US : PaymentBase, IPaymentUs { private IOCBDirect m_OCBDirect = null; public PunchhHappyPoint_US() { m_OCBDirect = (IOCBDirect)sManager.InitServiceInstance(ServiceLists.AGENT_OLEDEVICE.DLL, ServiceLists.AGENT_OLEDEVICE.OCB_DIRECT); } #region SearchPayment 결제 조회 /// /// 결제 조회 /// /// /// /// public string SearchPayment(string[] aParam, ref string[] aRet) { string sRet = UserCom.RST_ERR; return sRet; } #endregion #region GetPayment 결제 정보 획득 /// /// 결제 정보 획득 /// /// /// public object GetPayment(string[] aParam) { return m_cPayItem; } #endregion #region SetPayment 결제 등록 /// /// 결제 등록(제휴 포인트 사용) /// /// /// public string SetPayment(string[] aParam) { string sRet = UserCom.RST_ERR; return sRet; } #endregion #region CancelPayment 결제 취소 /// /// 결제 취소 /// /// /// public string CancelPayment(string[] aParam) { string sRet = UserCom.RST_ERR; return sRet; } #endregion #region RefundPayment 결제 반품 /// /// 결제 반품 /// /// /// public string RefundPayment(string[] aParam) { string sRet = UserCom.RST_ERR; return sRet; } #endregion #region SetMenualPayment public string SetMenualPayment(string[] aParam) { string sRet = UserCom.RST_ERR; return sRet; } #endregion } //##############################[결제, 포인트 관련 인터페이스 구현 클래스 끝]#############################################// #region Punchh 상수 public class PunchhConst { public const string LOCATION_KEY = "9079df560fad175e29c5dff19c5acbda";//"273b9a4039481b5063e1d1001cdaef4e"; public const string USER_LOOKUP_API_URL = "/users/search";//사용자 정보 조회 public static string POSSIBLE_REDEMPTION_API_URL = "/redemptions/possible"; // public static string CREATE_OR_VOID_REDEMPTION_API_URL = "/redemptions"; public static string CHECKIN_API_URL = "/checkins"; } #endregion #region 각종 요청/응답 모델 Strat (API 연동 시에 아래 모델 Json 형식으로 파싱함 #region 공통 모델 (Balance, Reward, MenuItem) Strat public class Balance { [JsonProperty(PropertyName = "banked_rewards")] public double BankedRewards { get; set; } [JsonProperty(PropertyName = "membership_level")] public string MembershipLevel { get; set; } [JsonProperty(PropertyName = "membership_level_id")] public int? MembershipLevelId { get; set; } [JsonProperty(PropertyName = "membership_program_id")] public int? MembershipProgramId { get; set; } [JsonProperty(PropertyName = "net_balance")] public double? NetBalance { get; set; } [JsonProperty(PropertyName = "net_debits")] public double? NetDebits { get; set; } [JsonProperty(PropertyName = "pending_points")] public double? PendingPoints { get; set; } [JsonProperty(PropertyName = "points_balance")] public double? PointsBalance { get; set; } [JsonProperty(PropertyName = "signup_anniversary_day")] public string SignupAnniversaryDay { get; set; } [JsonProperty(PropertyName = "total_credits")] public double? TotalCredits { get; set; } [JsonProperty(PropertyName = "total_debits")] public double? TotalDebits { get; set; } [JsonProperty(PropertyName = "total_point_credits")] public double? TotalPointCredits { get; set; } [JsonProperty(PropertyName = "total_redeemable_visits")] public int? TotalRedeemableVisits { get; set; } [JsonProperty(PropertyName = "expired_membership_level")] public string ExpiredMembershipLevel { get; set; } [JsonProperty(PropertyName = "total_visits")] public int? TotalVisits { get; set; } [JsonProperty(PropertyName = "initial_visits")] public int? InitialVisits { get; set; } [JsonProperty(PropertyName = "unredeemed_cards")] public int? UnredeemedCards { get; set; } } public class Reward { [JsonProperty(PropertyName = "id")] public int Id { get; set; } [JsonProperty(PropertyName = "name")] public string Name { get; set; } [JsonProperty(PropertyName = "description")] public string Description { get; set; } [JsonProperty(PropertyName = "discount_amount")] public double DiscountAmount { get; set; } [JsonProperty(PropertyName = "points")] public double Points { get; set; } [JsonProperty(PropertyName = "image")] public string Image { get; set; } [JsonProperty(PropertyName = "created_at")] public DateTime? CreatedAt { get; set; } [JsonProperty(PropertyName = "end_date_tz")] public DateTime? EndDateTz { get; set; } [JsonProperty(PropertyName = "start_date_tz")] public DateTime? StartDateTz { get; set; } [JsonProperty(PropertyName = "updated_at")] public DateTime? UpdatedAt { get; set; } [JsonProperty(PropertyName = "redeemable_properties")] public string RedeemableProperties { get; set; } [JsonProperty(PropertyName = "status")] public string Status { get; set; } } public class MenuItem { [JsonProperty(PropertyName = "item_name")] public string ItemName { get; set; } [JsonProperty(PropertyName = "item_qty")] public int? ItemQty { get; set; } [JsonProperty(PropertyName = "item_amount")] public double? ItemAmount { get; set; } [JsonProperty(PropertyName = "menu_item_type")] public string MenuItemType { get; set; } [JsonProperty(PropertyName = "menu_item_id")] public int? MenuItemId { get; set; } [JsonProperty(PropertyName = "menu_family")] public string MenuFamily { get; set; } [JsonProperty(PropertyName = "menu_major_group")] public string MenuMajorGroup { get; set; } [JsonProperty(PropertyName = "serial_number")] public int? SerialNumber { get; set; } } #endregion 공통 모델 (Balance, Reward, MenuItem) End #region UserLookup Model 사용자 정보, 포인트 조회 요청 파라메타 public class UserLookupRequest { [JsonProperty(PropertyName = "location_key")] public string LocationKey { get; set; } [JsonProperty(PropertyName = "phone")] public string Phone { get; set; } [JsonProperty(PropertyName = "user_as_qrcode")] public string UserAsQRcode { get; set; } [JsonProperty(PropertyName = "redemption_code")] public string RedemptionCode { get; set; } [JsonProperty(PropertyName = "card_number")] public string CardNumber { get; set; } } public class UserLookupResponse { [JsonProperty(PropertyName = "id")] public int Id { get; set; } [JsonProperty(PropertyName = "created_at")] public DateTime? CreatedAt { get; set; } [JsonProperty(PropertyName = "updated_at")] public DateTime? UpdatedAt { get; set; } [JsonProperty(PropertyName = "first_name")] public string FirstName { get; set; } [JsonProperty(PropertyName = "last_name")] public string LastName { get; set; } [JsonProperty(PropertyName = "phone")] public string Phone { get; set; } [JsonProperty(PropertyName = "fb_uid")] public string FbUid { get; set; } [JsonProperty(PropertyName = "birthday")] public DateTime? Birthday { get; set; } [JsonProperty(PropertyName = "gender")] public string Gender { get; set; } [JsonProperty(PropertyName = "city")] public string City { get; set; } [JsonProperty(PropertyName = "state")] public string State { get; set; } [JsonProperty(PropertyName = "selected_card_number")] public string SelectedCardNumber { get; set; } [JsonProperty(PropertyName = "selected_reward_id")] public int? selected_reward_id { get; set; } [JsonProperty(PropertyName = "balance")] public Balance Balance { get; set; } [JsonProperty(PropertyName = "rewards")] public List Rewards { get; set; } } #endregion UserLookup Model End #region Redemption Possible Model (결제 가능 여부 잔액 조회) public class RedemptionRequest { [JsonProperty(PropertyName = "location_key")] public string LocationKey { get; set; } [JsonProperty(PropertyName = "discount_type")] public string DiscountType { get; set; } [JsonProperty(PropertyName = "reward_id")] public int? RewardId { get; set; } [JsonProperty(PropertyName = "redemption_code")] public string RedemptionCode { get; set; } [JsonProperty(PropertyName = "user_as_qrcode")] public string UserAsQRcode { get; set; } [JsonProperty(PropertyName = "punchh_key")] public string PunchhKey { get; set; } [JsonProperty(PropertyName = "transaction_no")] public int? TransactionNo { get; set; } [JsonProperty(PropertyName = "receipt_datetime")] public DateTime? ReceiptDateTime { get; set; } [JsonProperty(PropertyName = "menu_items")] public List MenuItems { get; set; } [JsonProperty(PropertyName = "subtotal_amount")] public double? SubTotalAmount { get; set; } [JsonProperty(PropertyName = "receipt_amount")] public double? ReceiptAmount { get; set; } #region /* [JsonProperty(PropertyName = "email")] public string Email { get; set; } [JsonProperty(PropertyName = "cc_last4")] public int? CCLast4 { get; set; } [JsonProperty(PropertyName = "employee_id")] public int? EmployeeId { get; set; } [JsonProperty(PropertyName = "employee_name")] public string EmployeeName { get; set; } [JsonProperty(PropertyName = "external_uid")] public string ExternalUid { get; set; } [JsonProperty(PropertyName = "pos_version")] public string POSVersion { get; set; } [JsonProperty(PropertyName = "pos_type")] public string POSType { get; set; } [JsonProperty(PropertyName = "process")] public bool? Process { get; set; } * */ #endregion } public class RedemptionResponse { [JsonProperty(PropertyName = "redemption_id")] public int? RedemptionId { get; set; } [JsonProperty(PropertyName = "redemption_code")] public string RedemptionCode { get; set; } [JsonProperty(PropertyName = "redemption_amount")] public double? RedemptionAmount { get; set; } [JsonProperty(PropertyName = "status")] public string Status { get; set; } [JsonProperty(PropertyName = "category")] public string Category { get; set; } [JsonProperty(PropertyName = "previous_amount")] public string PreviousAmount { get; set; } } #endregion Redemption Possible Model End #region User Checkin Model , 해피포인트 적립 public class CheckinRequest { [JsonProperty(PropertyName = "location_key")] public string LocationKey { get; set; } [JsonProperty(PropertyName = "punchh_key")] public string PunchhKey { get; set; } [JsonProperty(PropertyName = "receipt_datetime")] public DateTime? ReceiptDateTime { get; set; } [JsonProperty(PropertyName = "payable")] public double? Payable { get; set; } [JsonProperty(PropertyName = "subtotal_amount")] public double SubTotalAmount { get; set; } [JsonProperty(PropertyName = "receipt_amount")] public double ReceiptAmount { get; set; } [JsonProperty(PropertyName = "pos_version")] public string POSVersion { get; set; } [JsonProperty(PropertyName = "employee_id")] public int EmployeeId { get; set; } [JsonProperty(PropertyName = "sequence_no")] public int SequenceNo { get; set; } [JsonProperty(PropertyName = "pos_type")] public string POSType { get; set; } [JsonProperty(PropertyName = "cc_last4")] public string CCLast4 { get; set; } [JsonProperty(PropertyName = "amount")] public string Amount { get; set; } [JsonProperty(PropertyName = "employee_name")] public string EmployeeName { get; set; } [JsonProperty(PropertyName = "transaction_no")] public int TransactionNo { get; set; } [JsonProperty(PropertyName = "revenue_code")] public string RevenueCode { get; set; } [JsonProperty(PropertyName = "menu_items")] public List MenuItems { get; set; } [JsonProperty(PropertyName = "phone")] public string Phone { get; set; } [JsonProperty(PropertyName = "email")] public string Email { get; set; } [JsonProperty(PropertyName = "user_as_qrcode")] public string UserAsQRcode { get; set; } [JsonProperty(PropertyName = "card_number")] public string CardNumber { get; set; } [JsonProperty(PropertyName = "single_scan_token")] public string SingleScanToken { get; set; } [JsonProperty(PropertyName = "redemption_code")] public string RedemptionCode { get; set; } [JsonProperty(PropertyName = "user_token")] public string UserToken { get; set; } } public class CheckinResponse { [JsonProperty(PropertyName = "id")] public string Id { get; set; } [JsonProperty(PropertyName = "first_name")] public string FirstName { get; set; } [JsonProperty(PropertyName = "last_name")] public string LastName { get; set; } [JsonProperty(PropertyName = "email")] public string Email { get; set; } [JsonProperty(PropertyName = "phone")] public string Phone { get; set; } [JsonProperty(PropertyName = "fb_uid")] public string FbUid { get; set; } [JsonProperty(PropertyName = "user_as_qrcode")] public string UserAsQRcode { get; set; } [JsonProperty(PropertyName = "birthday")] public string Birthday { get; set; } [JsonProperty(PropertyName = "gender")] public string Gender { get; set; } [JsonProperty(PropertyName = "anniversary")] public DateTime? Anniversary { get; set; } [JsonProperty(PropertyName = "created_at")] public DateTime? CreatedAt { get; set; } [JsonProperty(PropertyName = "rewards")] public List Rewards { get; set; } [JsonProperty(PropertyName = "balance")] public Balance Balance { get; set; } } #endregion User Checkin Model End #region Void Redemption Model 결제 취소 public class VoidRedemptionRequest { [JsonProperty(PropertyName = "location_key")] public string LocationKey { get; set; } [JsonProperty(PropertyName = "phone")] public string Phone { get; set; } [JsonProperty(PropertyName = "user_as_qrcode")] public string UserAsQRcode { get; set; } [JsonProperty(PropertyName = "card_number")] public string CardNumber { get; set; } [JsonProperty(PropertyName = "redemption_id")] public int? RedemptionId { get; set; } [JsonProperty(PropertyName = "redemption_code")] public string RedemptionCode { get; set; } [JsonProperty(PropertyName = "store_number")] public string StoreNumber { get; set; } [JsonProperty(PropertyName = "transaction_no")] public string TransactionNo { get; set; } } #endregion Void Redemption Model End #endregion End #region Punchh API 연동 시에 필요한 Library 정의 public static class PunchhLib { /// /// request json key-value /// /// /// /// public static NameValueCollection GetNameValue(T requestModel) { NameValueCollection nameValue = new NameValueCollection(); var lstRequest = requestModel.GetType().GetProperties().ToList(); foreach (var item in lstRequest) { var attributes = item.GetCustomAttributes(typeof(JsonPropertyAttribute), false); foreach (var att in attributes) { var atts = (JsonPropertyAttribute)att; var attName = atts.PropertyName;//json property var attValue = Convert.ToString(item.GetValue(requestModel, null)); nameValue.Add(attName, attValue); } } return nameValue; } /// /// http 통신 시 에 userAgent 정보 /// /// /// public static string GetUserAgent(string locationKey) { var windowsVersion = GetOSFriendlyName(); var dotNetVersion = System.Environment.Version; System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); System.Diagnostics.FileVersionInfo fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); var productVerArr = fvi.ProductVersion.Split('.'); string assemblyVersion = productVerArr[0] + "." + productVerArr[1] + "." + productVerArr[2]; StringBuilder sb = new StringBuilder(); sb.AppendFormat("pchposlib/{0};{1};{2};lkey:{3} ", assemblyVersion, dotNetVersion, windowsVersion, locationKey); return sb.ToString(); } /// /// OS 정보 /// /// public static string GetOSFriendlyName() { string result = string.Empty; result = "Windows"; /* 큰 의미 없어 보임. try { //System.Managemet 참조 추가 필요 ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem"); foreach (ManagementObject os in searcher.Get()) { result = os["Caption"].ToString(); break; } var resultArr = result.Split(' '); result = resultArr[1] + " " + resultArr[2]; } catch (Exception ex) { } * */ return result; } public static string GenerateXPchDigest(string message, string secretKey) { var encoding = new System.Text.ASCIIEncoding(); byte[] keyBytes = encoding.GetBytes(secretKey); byte[] messageBytes = encoding.GetBytes(message); using (var hmacSHA256 = new HMACSHA1(keyBytes)) { byte[] hashMessage = hmacSHA256.ComputeHash(messageBytes); return BitConverter.ToString(hashMessage).Replace("-", "").ToLower(); } } public static T ToEnum(this string value) { return (T)Enum.Parse(typeof(T), value, true); } public static int CreateTransactionNo() { TimeSpan ts = DateTime.UtcNow - new DateTime(2018, 1, 1, 0, 0, 0, 0); //double dTime = ts.TotalSeconds; int nTime = (int)Math.Truncate(ts.TotalSeconds); Random ran = new Random(); return nTime + ran.Next(0, 9999); } } #endregion //##############################[POS Client 기능 구현]#############################################// #region Punchh POS Config public class PunchhConfiguration { /// /// The Location Key. /// public string LocationKey { get; set; } /// /// Punchh Environment. /// public PunchhEnvironment Environment { get; set; } /// /// Client Secret. /// public string ClientSecret { get; set; } /// /// Is Retry Enabled. /// public bool IsRetryCalls { get; set; } /// /// The Region. /// public string Region { get; set; } } public enum PunchhEnvironment { SANDBOX, PRODUCTION } #endregion #region POS Client 기능 구현 public class PunchhPosClient { private string _locationKey { get; set; } private string _region { get; set; } /// The Base Url. //생성자 public PunchhPosClient(PunchhConfiguration configuration) { //클라이언트 부분 설정 _locationKey = configuration.LocationKey; _region = configuration.Region; SetAPIUrls(configuration); //서버 연동 부분 설정 if (!string.IsNullOrEmpty(configuration.ClientSecret)) { PunchhServiceCaller.ClientSecret = configuration.ClientSecret; PunchhServiceCaller.IsXPchEnabled = true; } PunchhServiceCaller.UserAgent = PunchhLib.GetUserAgent(configuration.LocationKey); PunchhServiceCaller.IsRetryCalls = configuration.IsRetryCalls; } public void SetAPIUrls(PunchhConfiguration configuration) { string baseUrl = string.Empty; string dataSinkUrl = string.Empty; switch (configuration.Region) { case "asia": baseUrl = "https://posasia.punchh.com"; dataSinkUrl = "https://islasia.punchh.com"; break; case "au": baseUrl = "https://posau.punchh.com"; dataSinkUrl = "https://islau.punchh.com"; break; case "eu": baseUrl = "https://poseu.punchh.com"; dataSinkUrl = "https://isleu.punchh.com"; break; case "uk": baseUrl = "https://posuk.punchh.com"; dataSinkUrl = "https://isluk.punchh.com"; break; default: baseUrl = "https://pos.punchh.com"; dataSinkUrl = "https://isl.punchh.com"; break; } if (configuration.Environment == PunchhEnvironment.SANDBOX) { baseUrl = "https://sandbox.punchh.com"; dataSinkUrl = "https://isl-sandbox.punchh.com"; } PunchhServiceCaller.BaseUrl = baseUrl + "/api/pos"; PunchhServiceCaller.DataSinkBaseUrl = dataSinkUrl; } //사용자 조회 public PunchhClientResponse UserLookup(UserLookupRequest requestModel) { try { NameValueCollection headers = new NameValueCollection(); headers.Add("Authorization", string.Format("Token token={0}", _locationKey)); NameValueCollection content = PunchhLib.GetNameValue(requestModel);//요청값 json key, value 로 변경 PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIRequest(PunchhConst.USER_LOOKUP_API_URL, headers, content); if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK) { var resp = JsonConvert.DeserializeObject(apiResponse.Message); return new PunchhClientResponse { Data = resp, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } else { return new PunchhClientResponse { Data = null, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } } catch (WebException ex) { return new PunchhClientResponse { Message = ex.Message, Status = ex.Status.ToString().ToEnum() }; } catch (Exception ex) { return new PunchhClientResponse { Data = null, Message = "Exception.", Status = PunchhResponseStatus.EXCEPTION }; } //return new PunchhResponse //{ // Data = null, // Status = PunchhResponseStatus.EXCEPTION, // Message = "Message" //}; } //해피 포인트 잔액, 결제 가능 금액 조회 public PunchhClientResponse RedemptionPossible(RedemptionRequest requestModel) { try { NameValueCollection headers = new NameValueCollection(); headers.Add("Authorization", string.Format("Token token={0}", _locationKey)); JObject jsonObj = JObject.FromObject(requestModel); string content = jsonObj.ToString(); PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIPostRequest(PunchhConst.POSSIBLE_REDEMPTION_API_URL, headers, content); if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK) { var resp = JsonConvert.DeserializeObject(apiResponse.Message); return new PunchhClientResponse { Data = resp, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } else { return new PunchhClientResponse { Data = null, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } } catch (WebException ex) { return new PunchhClientResponse { Message = ex.Message, Status = ex.Status.ToString().ToEnum() }; } catch (Exception ex) { return new PunchhClientResponse { Data = null, Message = "Exception.", Status = PunchhResponseStatus.EXCEPTION }; } } //해피 포인트 결제 public PunchhClientResponse RedemptionCreate(RedemptionRequest requestModel) { try { NameValueCollection headers = new NameValueCollection(); headers.Add("Authorization", string.Format("Token token={0}", _locationKey)); JObject jsonObj = JObject.FromObject(requestModel); string content = jsonObj.ToString(); PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIPostRequest(PunchhConst.CREATE_OR_VOID_REDEMPTION_API_URL, headers, content); if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK) { var resp = JsonConvert.DeserializeObject(apiResponse.Message); return new PunchhClientResponse { Data = resp, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } else { return new PunchhClientResponse { Data = null, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } } catch (WebException ex) { return new PunchhClientResponse { Message = ex.Message, Status = ex.Status.ToString().ToEnum() }; } catch (Exception ex) { return new PunchhClientResponse { Data = null, Message = "Exception.", Status = PunchhResponseStatus.EXCEPTION }; } } // 해피 포인트 적립 public PunchhClientResponse UserCheckin(CheckinRequest requestModel) { try { NameValueCollection headers = new NameValueCollection(); headers.Add("Authorization", string.Format("Token token={0}", _locationKey)); JObject jsonObj = JObject.FromObject(requestModel); string content = jsonObj.ToString(); PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIPostRequest(PunchhConst.CHECKIN_API_URL, headers, content); if (apiResponse.HttpStatusCode == System.Net.HttpStatusCode.OK) { var resp = JsonConvert.DeserializeObject(apiResponse.Message); return new PunchhClientResponse { Data = resp, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } else { return new PunchhClientResponse { Data = null, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } } catch (WebException ex) { return new PunchhClientResponse { Message = ex.Message, Status = ex.Status.ToString().ToEnum() }; } catch (Exception ex) { return new PunchhClientResponse { Data = null, Message = "Exception.", Status = PunchhResponseStatus.EXCEPTION }; } } // 해피 포인트 결제 취소 public PunchhClientResponse VoidRedemption(VoidRedemptionRequest requestModel) { try { NameValueCollection headers = new NameValueCollection(); headers.Add("Authorization", string.Format("Token token={0}", _locationKey)); NameValueCollection content = PunchhLib.GetNameValue(requestModel); PunchhAPIResponse apiResponse = PunchhServiceCaller.PunchhAPIDeleteRequest(PunchhConst.CREATE_OR_VOID_REDEMPTION_API_URL, headers, content); return new PunchhClientResponse { Data = string.Empty, Message = apiResponse.Message, Status = apiResponse.HttpStatusCode.ToString().ToEnum() }; } catch (WebException ex) { return new PunchhClientResponse { Message = ex.Message, Status = ex.Status.ToString().ToEnum() }; } catch (Exception e) { return new PunchhClientResponse { Data = null, Message = "Exception : ", Status = PunchhResponseStatus.EXCEPTION }; } } } #endregion #region Punchh Server API 에서 응답 후 POS Client 형식으로 변경 //각 기능 응답 제네릭 클래스 //API 에서 리턴 되어 오는 형식 그대로 에서 각 요청 메뉴 별로 형태 변경 public class PunchhClientResponse { public T Data { get; set; } public PunchhResponseStatus Status { get; set; } public string Message { get; set; } } public enum PunchhResponseStatus { OK = 200, CREATED = 201, ACCEPTED = 202, NotModified = 304, NOTFOUND = 404, BADREQUEST = 400, UNAUTHORIZED = 401, PRECONDITIONFAILED = 412, UNPROCESSABLE_ENTITY = 422, INTERNAL_SERVER_ERROR = 500, EXCEPTION = -1, PARAMETERS_NOT_SET = -2, TIMEOUT = -3, NAMERESOLUTIONFAILURE = -4, CONNECTFAILURE = -5 } #endregion //##############################[POS Client 기능 구현 끝]##########################################// //###############################[Punchh Server API 호출]##########################################// #region Punchh Server API 호출 public static class PunchhServiceCaller { #region //속성의 값들은 PunchhPosClient 에서 호출 시 설정함. public static string BaseUrl { get; set; } public static string DataSinkBaseUrl { get; set; } public static bool IsXPchEnabled { get; set; } public static string ClientSecret { get; set; }//현재는 사용 안 되는 함. public static string UserAgent { get; set; } public static bool IsRetryCalls { get; set; } // 서버 오류 발생시 재요청 여부 public static bool IsDataSinkCall { get; set; } // Is Receipt Datasink Call. #endregion #region Request API public static PunchhAPIResponse PunchhAPIRequest(string apiURL, NameValueCollection headers = null, NameValueCollection content = null) { int retryCall = 3; while (true) { try { //요청 #region string queryString = string.Empty; if (content != null) { queryString = string.Join("&", content.AllKeys.Where(key => !string.IsNullOrWhiteSpace(content[key])).Select(key => string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key) , System.Web.HttpUtility.UrlEncode(content[key]) ))); queryString = "?" + queryString; } string punchhAPI_URL = BaseUrl + apiURL + queryString; HttpWebRequest.DefaultCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Revalidate); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(punchhAPI_URL); request.ContentType = "application/x-www-form-urlencoded"; request.Method = "GET"; request.Timeout = 30000; request.UserAgent = PunchhLib.GetUserAgent(UserAgent); request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Revalidate); if (IsXPchEnabled && string.IsNullOrEmpty(ClientSecret))//ClientSecret 가 사용 되지 않음므로 해당 사항 없음 { Uri uri = new Uri(punchhAPI_URL); string sPath = uri.AbsolutePath; string sBody = queryString; string sValue = sPath + sBody; string xPchDigest = PunchhLib.GenerateXPchDigest(sValue, ClientSecret); headers.Add("x-pch-digest", xPchDigest); } if (headers != null) { foreach (var key in headers.AllKeys) { request.Headers[key] = headers[key];//웹통신에 header 추가 } } #endregion //응답 #region string responseMessage = string.Empty; HttpWebResponse response = null; response = (HttpWebResponse)request.GetResponse(); using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream)) { responseMessage = reader.ReadToEnd(); } } #endregion retryCall = 0; return new PunchhAPIResponse { Message = responseMessage, HttpStatusCode = response.StatusCode, Headers = response.Headers }; } catch (WebException ex) { retryCall--; if (ex.Response != null) { #region 재시도 후 결과 전송 var webResp = (HttpWebResponse)ex.Response; var respMsg = new StreamReader(webResp.GetResponseStream()).ReadToEnd(); if (IsRetryCalls && retryCall > 0 && webResp.StatusCode == HttpStatusCode.InternalServerError) continue; retryCall = 0; string filteredRespMsg = string.Empty; try { filteredRespMsg = (respMsg.Contains("error")) ? JsonConvert.DeserializeObject(respMsg).Error.Message : JsonConvert.DeserializeObject(respMsg)[0].ToString(); } catch (Exception e) { //e.Message; } return new PunchhAPIResponse { Message = string.IsNullOrEmpty(filteredRespMsg) ? ex.Message : filteredRespMsg, HttpStatusCode = webResp.StatusCode, Headers = webResp.Headers }; #endregion } else { if (IsRetryCalls && retryCall > 0) continue; else throw ex; } } catch (Exception ex) { throw ex; } }//while end } public static PunchhAPIResponse PunchhAPIPostRequest(string apiURL, NameValueCollection headers, string content) { int retryCall = 3; while (true) { try { #region string punchhAPI_URL = BaseUrl + apiURL; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(punchhAPI_URL); request.ContentType = "application/json"; request.Method = "POST"; request.Timeout = 30000; request.UserAgent = PunchhLib.GetUserAgent(UserAgent); if (IsXPchEnabled && !string.IsNullOrEmpty(ClientSecret) && !IsDataSinkCall) { Uri uri = new Uri(punchhAPI_URL); string sPath = uri.AbsolutePath; string sBody = content; string sValue = sPath + sBody; string xPchDigest = PunchhLib.GenerateXPchDigest(sValue, ClientSecret); headers.Add("x-pch-digest", xPchDigest); } if (headers != null) { foreach (var key in headers.AllKeys) { request.Headers[key] = headers[key]; } } // Convert post data into bytes. byte[] contentBytes = Encoding.ASCII.GetBytes(content); // Set post data to request stream. using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(contentBytes, 0, contentBytes.Length); requestStream.Close(); } string responseMessage = string.Empty; HttpWebResponse response = null; response = (HttpWebResponse)request.GetResponse(); using (Stream stream = response.GetResponseStream()) using (StreamReader reader = new StreamReader(stream)) { responseMessage = reader.ReadToEnd(); // Logger.Log("Response Body: " + responseMessage, LogType.INFO); } retryCall = 0; return new PunchhAPIResponse { Message = responseMessage, HttpStatusCode = response.StatusCode, Headers = response.Headers }; #endregion } catch (WebException ex) { #region retryCall--; if (ex.Response != null) { #region 재시도 후 결과 전송 var webResp = (HttpWebResponse)ex.Response; var respMsg = new StreamReader(webResp.GetResponseStream()).ReadToEnd(); if (IsRetryCalls && retryCall > 0 && webResp.StatusCode == HttpStatusCode.InternalServerError) continue; retryCall = 0; string filteredRespMsg = string.Empty; try { filteredRespMsg = (respMsg.Contains("error")) ? JsonConvert.DeserializeObject(respMsg).Error.Message : JsonConvert.DeserializeObject(respMsg)[0].ToString(); } catch (Exception e) { //e.Message; } return new PunchhAPIResponse { Message = string.IsNullOrEmpty(filteredRespMsg) ? ex.Message : filteredRespMsg, HttpStatusCode = webResp.StatusCode, Headers = webResp.Headers }; #endregion } else { if (IsRetryCalls && retryCall > 0) continue; else throw ex; } #endregion }// try end }//while end } public static PunchhAPIResponse PunchhAPIDeleteRequest(string apiURL, NameValueCollection headers, NameValueCollection content) { int retryCall = 3; while (true) { try { #region string queryString = string.Empty; if (content != null) { queryString = string.Join("&", content.AllKeys.Where(key => !string.IsNullOrWhiteSpace(content[key])).Select(key => string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key, Encoding.UTF8), System.Web.HttpUtility.UrlEncode(content[key], Encoding.UTF8)))); queryString = "?" + queryString; } string punchhAPI_URL = BaseUrl + apiURL + queryString; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(punchhAPI_URL); request.ContentType = "application/x-www-form-urlencoded"; request.Method = "DELETE"; request.Timeout = 30000; request.UserAgent = PunchhLib.GetUserAgent(UserAgent); if (IsXPchEnabled) { Uri uri = new Uri(punchhAPI_URL); string sPath = uri.AbsolutePath; string sBody = queryString; string sValue = sPath + sBody; string xPchDigest = PunchhLib.GenerateXPchDigest(sValue, ClientSecret); headers.Add("x-pch-digest", xPchDigest); } if (headers != null) { foreach (var key in headers.AllKeys) { request.Headers[key] = headers[key];//웹통신에 header 추가 } } #endregion //응답 #region string responseMessage = string.Empty; HttpWebResponse response = null; response = (HttpWebResponse)request.GetResponse(); using (Stream stream = response.GetResponseStream()) using (StreamReader reader = new StreamReader(stream)) { responseMessage = reader.ReadToEnd(); } #endregion retryCall = 0; return new PunchhAPIResponse { Message = responseMessage, HttpStatusCode = response.StatusCode, Headers = response.Headers }; }//end try catch (WebException ex) { retryCall--; if (ex.Response != null) { #region 재시도 후 결과 전송 var webResp = (HttpWebResponse)ex.Response; var respMsg = new StreamReader(webResp.GetResponseStream()).ReadToEnd(); if (IsRetryCalls && retryCall > 0 && webResp.StatusCode == HttpStatusCode.InternalServerError) continue; retryCall = 0; string filteredRespMsg = string.Empty; try { filteredRespMsg = (respMsg.Contains("error")) ? JsonConvert.DeserializeObject(respMsg).Error.Message : JsonConvert.DeserializeObject(respMsg)[0].ToString(); } catch (Exception e) { //e.Message; } return new PunchhAPIResponse { Message = string.IsNullOrEmpty(filteredRespMsg) ? ex.Message : filteredRespMsg, HttpStatusCode = webResp.StatusCode, Headers = webResp.Headers }; #endregion } else { if (IsRetryCalls && retryCall > 0) continue; else throw ex; } } catch (Exception ex) { throw ex; }//end catch }//while end } #endregion } #endregion #region Punchh Server API Response 형식 정의 (Punchh API 에서 아래 형식으로 리턴함) public class PunchhAPIResponse { public System.Net.WebHeaderCollection Headers { get; set; } public HttpStatusCode HttpStatusCode { get; set; } public string Message { get; set; } } public class PunchhErrorResponse { public Error Error { get; set; } } public class Error { public string Message { get; set; } } #endregion //###############################[Punchh Server API 호출 끝]#######################################// }