You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

534 lines
23 KiB

  1. using Common.Models.Bank_Account;
  2. using Common.Models.Donga;
  3. using Common.Models.RequestResponse;
  4. using Common.Models.Status;
  5. using Common.Models.TxnModel;
  6. using Common.TPService;
  7. using Common.Utility;
  8. using log4net;
  9. using Newtonsoft.Json;
  10. using Org.BouncyCastle.Crypto.Parameters;
  11. using Org.BouncyCastle.OpenSsl;
  12. using Org.BouncyCastle.Security;
  13. using RestSharp;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.IO;
  17. using System.Text;
  18. using System.Web.Hosting;
  19. namespace Donga.DongaAPIService
  20. {
  21. public class DongApiV2 : ITPApiServices
  22. {
  23. private readonly ILog _log = LogManager.GetLogger(typeof(DongApiV2));
  24. protected string baseUrl { get; set; }
  25. protected string partnerId { get; set; }
  26. protected string secretKey { get; set; }
  27. protected string privatekey { get; set; }
  28. private const string ACCOUNT_VERIFY = "/restapi/account/verify";
  29. private const string TRANSFER_SENDMASTER = "/restapi/transfer/sendmaster";
  30. private const string TRANSFER_SENDDETAIL = "/restapi/transfer/senddetail";
  31. private const string TRANSFER_ONLINE = "/restapi/transfer/online";
  32. private const string TRANSFER_STATUS = "/restapi/transfer/tracking";
  33. public DongApiV2()
  34. {
  35. baseUrl = GetStatic.ReadWebConfig("dongav2_base_url", "");
  36. partnerId = GetStatic.ReadWebConfig("dongav2_partnerId", "");
  37. secretKey = GetStatic.ReadWebConfig("dongav2_secretKey", "");
  38. privatekey = GetStatic.ReadWebConfig("dongav2_privateKey", "");
  39. }
  40. public TPResponse GetTPResponse<T>(T model, string MethodName) where T : class
  41. {
  42. switch (MethodName)
  43. {
  44. case "send":
  45. return SendTransaction(model as SendTransaction);
  46. case "verify":
  47. return AccountVerifyDongav2(model as AccountValidate);
  48. case "status":
  49. return GetStatusDongaV2(model as GetStatus);
  50. default:
  51. throw new NotImplementedException();
  52. }
  53. }
  54. private TPResponse SendTransaction(SendTransaction model)
  55. {
  56. TPResponse _response = new TPResponse();
  57. string sendTxnURL = "";
  58. if (model.IsAccValSupported && model.Transaction.PaymentType.ToLower() == "bank deposit")
  59. {
  60. sendTxnURL = TRANSFER_ONLINE;
  61. AccountValidate _validateRequest = new AccountValidate
  62. {
  63. AccountNumber = model.Receiver.RAccountNo,
  64. BankCode = model.Agent.PBankId,
  65. ReceiverName = model.Receiver.RFullName,
  66. ControlNo = model.Transaction.JMEControlNo
  67. };
  68. _response = AccountVerifyDongav2(_validateRequest);
  69. if (_response.ResponseCode == "0")
  70. {
  71. var _request = MapDongaRequestData(model, _response.Extra, "online");
  72. _response = SendTransactionDonga(_request, "online", sendTxnURL);
  73. }
  74. }
  75. else
  76. {
  77. sendTxnURL = TRANSFER_SENDDETAIL;
  78. _response = SendMasterData(model);
  79. if (_response.ResponseCode == "0")
  80. {
  81. var _request = MapDongaRequestData(model, _response.Extra, "other");
  82. _response = SendTransactionDonga(_request, "other", sendTxnURL);
  83. }
  84. }
  85. return _response;
  86. }
  87. private TPResponse SendTransactionDonga<T>(T model, string sendType, string url)
  88. {
  89. TPResponse _response = new TPResponse();
  90. try
  91. {
  92. var client = new RestClient(baseUrl);
  93. string request = "";
  94. if (sendType == "online")
  95. request = SimpleJson.SerializeObject(model as OnlineTransferRequest);
  96. else
  97. request = SimpleJson.SerializeObject(model as List<DetailDataRequest>);
  98. string signature = SignData(request);
  99. _log.Info($"{url}|REQUEST : {request}");
  100. var result = client.Execute<MasterDataResponse>(BuildRequest<string>(url, request, signature));
  101. _log.Info($"{url}|RESPONSE : {result.Content}");
  102. if (result.StatusCode == System.Net.HttpStatusCode.OK)
  103. {
  104. var res = result.Data;
  105. _response.Data = result.Data;
  106. if (!string.IsNullOrEmpty(res.ResponseCode) && res.ResponseCode.Equals("0000"))
  107. {
  108. _response.ResponseCode = "0";
  109. _response.Msg = "Success";
  110. }
  111. else
  112. {
  113. _response.ResponseCode = "1";
  114. _response.Extra = res.ResponseCode;
  115. _response.Msg = $"ErrorCode: {res.ResponseCode }. { (ResponseCodes.ContainsKey(res.ResponseCode) ? ResponseCodes[res.ResponseCode] : "")}";
  116. }
  117. }
  118. else
  119. {
  120. _response.ResponseCode = "1";
  121. _response.Msg = string.IsNullOrEmpty(result.Content) ? result.ErrorMessage : string.Join(",", SimpleJson.DeserializeObject<DongaV2Error>(result.Content).Errors);
  122. }
  123. }
  124. catch (Exception ex)
  125. {
  126. _response.ResponseCode = "999";
  127. _response.Msg = ex.Message;
  128. }
  129. return _response;
  130. }
  131. private TPResponse SendMasterData(SendTransaction model)
  132. {
  133. TPResponse _response = new TPResponse();
  134. try
  135. {
  136. var client = new RestClient(baseUrl);
  137. MasterDataRequest masterDataRequest = new MasterDataRequest()
  138. {
  139. SumTransaction = 1,
  140. PartnerId = partnerId,
  141. SumAUD = (model.Transaction.PCurr.ToLower().Equals("aud")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0,
  142. SumCAD = (model.Transaction.PCurr.ToLower().Equals("cad")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0,
  143. SumEUR = (model.Transaction.PCurr.ToLower().Equals("eur")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0,
  144. SumGBP = (model.Transaction.PCurr.ToLower().Equals("gbp")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0,
  145. SumJPY = (model.Transaction.PCurr.ToLower().Equals("jpy")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0,
  146. SumUSD = (model.Transaction.PCurr.ToLower().Equals("usd")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0,
  147. SumVND = (model.Transaction.PCurr.ToLower().Equals("vnd")) ? Convert.ToDecimal(model.Transaction.PAmt) : 0
  148. };
  149. string requestString = SimpleJson.SerializeObject(masterDataRequest);
  150. string signature = SignData(requestString);
  151. _log.Info($"{TRANSFER_SENDMASTER}|REQUEST : {requestString}");
  152. var result = client.Execute<MasterDataResponse>(BuildRequest<MasterDataRequest>(TRANSFER_SENDMASTER, masterDataRequest, signature));
  153. _log.Info($"{TRANSFER_SENDMASTER}|RESPONSE : {result.Content}");
  154. if (result.StatusCode == System.Net.HttpStatusCode.OK)
  155. {
  156. var res = result.Data;
  157. _response.Data = result.Data;
  158. if (!string.IsNullOrEmpty(res.ResponseCode) && res.ResponseCode.Equals("0000"))
  159. {
  160. _response.ResponseCode = "0";
  161. _response.Msg = "Success";
  162. _response.Extra = result.Data.ApiKey;
  163. }
  164. else
  165. {
  166. _response.ResponseCode = "1";
  167. _response.Msg = $"ErrorCode: {res.ResponseCode }. { (ResponseCodes.ContainsKey(res.ResponseCode) ? ResponseCodes[res.ResponseCode] : "")}";
  168. }
  169. }
  170. else
  171. {
  172. _response.ResponseCode = "1";
  173. _response.Msg = string.IsNullOrEmpty(result.Content) ? result.ErrorMessage : string.Join(",", SimpleJson.DeserializeObject<DongaV2Error>(result.Content).Errors);
  174. }
  175. }
  176. catch (Exception ex)
  177. {
  178. _response.ResponseCode = "999";
  179. _response.Msg = ex.Message;
  180. }
  181. return _response;
  182. }
  183. private object MapDongaRequestData(SendTransaction model, string apiKey, string type)
  184. {
  185. if (type == "online")
  186. {
  187. return new OnlineTransferRequest
  188. {
  189. PartnerID = partnerId,
  190. ApiKey = apiKey,
  191. TransactionId = model.Transaction.JMEControlNo,
  192. SenderName = model.Sender.SFullName,
  193. BenName = model.Receiver.RFullName,
  194. BenAddress = model.Receiver.RAdd1.Trim(),
  195. CityCode = "002", //Default
  196. DistrictCode = model.Receiver.RCityCode,
  197. Amount = model.Transaction.PAmt,
  198. SCurrency = model.Transaction.PCurr,
  199. PCurrency = model.Transaction.PCurr,
  200. PaymentMode = GetPayoutMode(model.Transaction.PaymentType),
  201. TransferType = "OT",
  202. BenPhone = model.Receiver.RMobile,
  203. BenAccount = model.Receiver.RAccountNo,
  204. AccountType = "ACC",
  205. BankCode = model.Agent.PBankId,
  206. BenRelationship = model.Receiver.RelWithSenderName,
  207. MoneyPurpose = model.Transaction.PurposeOfRemittanceName
  208. };
  209. }
  210. else
  211. {
  212. List<DetailDataRequest> _dongaSendTxnRequestData = new List<DetailDataRequest>();
  213. var pm = GetPayoutMode(model.Transaction.PaymentType);
  214. _dongaSendTxnRequestData.Add(new DetailDataRequest
  215. {
  216. ApiKey = apiKey,
  217. TransactionId = model.Transaction.JMEControlNo,
  218. SenderName = model.Sender.SFullName,
  219. BenName = model.Receiver.RFullName,
  220. BenAddress = model.Receiver.RAdd1.Trim(),
  221. CityCode = pm.Equals("TA") ? "002" : model.Receiver.RStateId,
  222. DistrictCode = model.Receiver.RCityCode,
  223. Amount = model.Transaction.PAmt,
  224. SCurrency = model.Transaction.PCurr,
  225. PCurrency = model.Transaction.PCurr,
  226. PaymentMode = pm,
  227. BenPhone = model.Receiver.RMobile,
  228. BenAccount = pm.Equals("TA") ? model.Receiver.RAccountNo : null,
  229. BankCode = pm.Equals("TA") ? model.Agent.PBankId : null,
  230. Message = model.Transaction.PayoutMsg,
  231. BenRelationship = model.Receiver.RelWithSenderName,
  232. MoneyPurpose = model.Transaction.PurposeOfRemittanceName,
  233. // IsBankTransfer = pm.Equals("TA") ? true : false
  234. });
  235. return _dongaSendTxnRequestData;
  236. }
  237. }
  238. private string GetPayoutMode(string paymentType)
  239. {
  240. if (paymentType.ToLower().Equals("cash payment"))
  241. return "CP";
  242. else if (paymentType.ToLower().Equals("home delivery"))
  243. return "HD";
  244. else if (paymentType.ToLower().Equals("bank deposit"))
  245. return "TA";
  246. return "";
  247. }
  248. private TPResponse AccountVerifyDongav2(AccountValidate model)
  249. {
  250. TPResponse _response = new TPResponse();
  251. try
  252. {
  253. var client = new RestClient(baseUrl);
  254. AccountVerifyRequest accountVerifyRequest = new AccountVerifyRequest()
  255. {
  256. AccountType = "ACC",
  257. BankCode = model.BankCode,
  258. BenAccount = model.AccountNumber,
  259. PartnerId = partnerId
  260. };
  261. var requestJson = JsonConvert.SerializeObject(accountVerifyRequest, Formatting.None);
  262. _log.Info($"{ACCOUNT_VERIFY}|REQUEST : {requestJson}");
  263. var result = client.Execute<AccountVerifyResponse>(BuildRequest<AccountVerifyRequest>(ACCOUNT_VERIFY, accountVerifyRequest, SignData(requestJson)));
  264. _log.Info($"{ACCOUNT_VERIFY}|RESPONSE : {result.Content}");
  265. if (result.StatusCode == System.Net.HttpStatusCode.OK)
  266. {
  267. var res = result.Data;
  268. _response.Data = result.Data;
  269. if (!string.IsNullOrEmpty(res.ResponseCode) && res.ResponseCode.Equals("0000"))
  270. {
  271. _response.ResponseCode = "0";
  272. _response.Msg = "Success";
  273. _response.Extra = res.ApiKey;
  274. _response.Extra2 = res.BenName;
  275. }
  276. else
  277. {
  278. _response.ResponseCode = "1";
  279. if (res.ResponseCode.Equals("0001"))
  280. {
  281. _response.Msg = $"ErrorCode: {res.ResponseCode }. Account validation failed!. Please check Bank/Account number";
  282. }
  283. else
  284. _response.Msg = $"ErrorCode: {res.ResponseCode }. { (ResponseCodes.ContainsKey(res.ResponseCode) ? ResponseCodes[res.ResponseCode] : "")}";
  285. }
  286. }
  287. else
  288. {
  289. _response.ResponseCode = "1";
  290. _response.Msg = string.IsNullOrEmpty(result.Content) ? result.ErrorMessage : string.Join(",", SimpleJson.DeserializeObject<DongaV2Error>(result.Content).Errors);
  291. }
  292. }
  293. catch (Exception ex)
  294. {
  295. _response.ResponseCode = "999";
  296. _response.Msg = ex.Message;
  297. }
  298. return _response;
  299. }
  300. private TPResponse GetStatusDongaV2(GetStatus model)
  301. {
  302. TPResponse _response = new TPResponse();
  303. try
  304. {
  305. var client = new RestClient(baseUrl);
  306. TrackingStatusRequest statusRequest = new TrackingStatusRequest()
  307. {
  308. TransactionID = model.ControlNo,
  309. PartnerID = partnerId
  310. };
  311. var requestJson = JsonConvert.SerializeObject(statusRequest, Formatting.None);
  312. _log.Info($"{TRANSFER_STATUS}|REQUEST : {requestJson}");
  313. var result = client.Execute<TrackingStatusResponse>(BuildRequest<TrackingStatusRequest>(TRANSFER_STATUS, statusRequest, SignData(requestJson)));
  314. _log.Info($"{TRANSFER_STATUS}|RESPONSE : {result.Content}");
  315. if (result.StatusCode == System.Net.HttpStatusCode.OK)
  316. {
  317. var res = result.Data;
  318. _response.Data = result.Data;
  319. if (!string.IsNullOrEmpty(res.ResponseCode) && res.ResponseCode.Equals("0000"))
  320. {
  321. _response.ResponseCode = "0";
  322. _response.Extra2 = StatusCodes.ContainsKey(res.Status) ? StatusCodes[res.Status] : "Unpaid";
  323. _response.Msg = $"{res.Status }: { (StatusCodes.ContainsKey(res.Status) ? StatusCodes[res.Status] : "")}";
  324. if (!string.IsNullOrEmpty(res.PaidDate))
  325. _response.Msg += $"Paid Date : {res.PaidDate} | ";
  326. if (!string.IsNullOrEmpty(res.UnpaidReason))
  327. _response.Msg += $"Unpaid Reason: { res.UnpaidReason}";
  328. }
  329. else
  330. {
  331. _response.ResponseCode = "1";
  332. _response.Msg = !string.IsNullOrEmpty(res.Status) ? $"Code: {res.Status}| Reason: {(StatusCodes.ContainsKey(res.Status) ? StatusCodes[res.Status] : result.Content)}" : result.Content;
  333. }
  334. }
  335. else
  336. {
  337. _response.ResponseCode = "1";
  338. _response.Msg = string.IsNullOrEmpty(result.Content) ? result.ErrorMessage : string.Join(",", SimpleJson.DeserializeObject<DongaV2Error>(result.Content).Errors);
  339. }
  340. }
  341. catch (Exception ex)
  342. {
  343. _response.ResponseCode = "999";
  344. _response.Msg = ex.Message;
  345. }
  346. return _response;
  347. }
  348. private RestRequest BuildRequest<T>(string action, T o, string signature = "", Method method = Method.POST) where T : class
  349. {
  350. var authorizeHeader = "Basic " + Base64Encode($"{ partnerId}:{secretKey}");
  351. var request = new RestRequest(action, method);
  352. request.AddHeader("Content-Type", "application/json");
  353. if (!string.IsNullOrEmpty(authorizeHeader))
  354. request.AddHeader("Authorization", authorizeHeader);
  355. if (!string.IsNullOrEmpty(signature))
  356. request.AddHeader("Signature", signature);
  357. request.RequestFormat = DataFormat.Json;
  358. if (o != null)
  359. request.AddJsonBody(o);
  360. return request;
  361. }
  362. #region Helpers
  363. private static string Base64Encode(string plainText)
  364. {
  365. var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
  366. return Convert.ToBase64String(plainTextBytes);
  367. }
  368. public string SignData(string plainText)
  369. {
  370. var privateKey = File.ReadAllText(HostingEnvironment.MapPath($"~/{privatekey}"));
  371. var ms = new MemoryStream(Encoding.Default.GetBytes(privateKey));
  372. var sr = new StreamReader(ms);
  373. var pemReader = new PemReader(sr);
  374. var key = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
  375. var signer = SignerUtilities.GetSigner("SHA256withRSA");
  376. signer.Init(true, key);
  377. var bytes = Encoding.UTF8.GetBytes(plainText);
  378. signer.BlockUpdate(bytes, 0, bytes.Length);
  379. byte[] signature = signer.GenerateSignature();
  380. return Convert.ToBase64String(signature);
  381. }
  382. #endregion
  383. public static Dictionary<string, string> ResponseCodes = new Dictionary<string, string>()
  384. {
  385. { "0000", "SUCCESS"},
  386. { "0001", "Unexpected error."},
  387. { "0002", "Records missedmatch"},
  388. { "0003", "Account is incorrect"},
  389. { "0004", "Invalid data."},
  390. { "0005", "Invalid API KEY"},
  391. { "0006", "Duplicate records."},
  392. { "0007", "Total of amount missed match"},
  393. { "0008", "TIME OUT"},
  394. { "0009", "NO DATA FOUND"},
  395. { "0010", "Invalid Data"},
  396. { "0012", "Invalid transaction."},
  397. { "0013", "Invalid amount."},
  398. { "0014", "Invalid card or Account number (no such number)"},
  399. { "0015", "Not sufficient funds"},
  400. { "0016", "Http method is not Allow"},
  401. { "0017", "Invalid capture date"},
  402. { "0019", "Please check with issuing bank."},
  403. { "0021", "Card not initialized"},
  404. { "0030", "Message Format Error"},
  405. { "0034", "Suspected Fraud"},
  406. { "0039", "No Credit account"},
  407. { "0041", "Pick up card (Lost card)"},
  408. { "0042", "No universal account"},
  409. { "0043", "Stolen card, pick-up"},
  410. { "0051", "System is busy. Try again later"},
  411. { "0054", "Expire Card"},
  412. { "0057", "Transaction not permitted to cardholder"},
  413. { "0059", "Suspected Fraud"},
  414. { "0062", "Restricted Card"},
  415. { "0064", "Original Amount Incorrect"},
  416. { "0066", "Exceeds Acquirer Limit"},
  417. { "0068", "Response received too late (time-out)"},
  418. { "0075", "Allowable number of PIN tries exceeded"},
  419. { "0076", "Invalid Account."},
  420. { "0090", "Cut-off is in progress"},
  421. { "0091", "Issuer or switch is in operation"},
  422. { "0092", "Financial institution or intermediate network facility cannot be found for routing"},
  423. { "0093", "Invalid Acquirer"},
  424. { "0094", "Duplicate Transaction"},
  425. { "0096", "System denied transaction, please try again later."},
  426. //{ "0099", "Please check with issuing bank."},
  427. { "0103", "Transaction does not exist"},
  428. { "0105", "Invalid Transfer Account Type"},
  429. { "0106", "CITAD only support working time & business date"},
  430. { "1111", "Internal Error"},
  431. { "1002", "IBT Transfer to Our bank card"},
  432. { "1003", "There are no app payments information registered"},
  433. { "1004", "The transaction has been canceled due to an error of authentication number three time or more"},
  434. { "1005", "Transaction error, please make transaction again."},
  435. { "1006", "Please check with issuing bank."},
  436. { "1007", "This request is not able to process Please apply for a new one"},
  437. { "1010", "Currency not allowed"},
  438. { "1011", "Operation account number is mandatory."},
  439. { "1021", "In-Active Card"},
  440. { "0025", "Unable to locate record on file"},
  441. { "1030", "Transaction information is invalid."},
  442. { "1040", "Requested function not supported"},
  443. { "1053", "Deposit limit account"},
  444. { "0061", "The transfer amount is over allowed limit."},
  445. { "0065", "Allowable number of Transactions Count exceeded"},
  446. //{ "1075", "Please check with issuing bank."},
  447. { "3001", "Communication"},
  448. { "3002", "Identify"},
  449. { "3003", "Message parsing"},
  450. { "3004", "Connect Error"},
  451. { "3005", "No Channel"},
  452. { "3006", "Time over"},
  453. { "3007", "Closed"},
  454. { "3008", "No Adapter"},
  455. { "9997", "Create signature error"},
  456. { "9998", "Validation signature error"},
  457. { "9999", "Authorization error"}
  458. };
  459. public static Dictionary<string, string> StatusCodes = new Dictionary<string, string>()
  460. {
  461. { "001", "WAITING"},
  462. { "002", "IN PROCESS "},
  463. { "003", "PAID"},
  464. { "004", "CANCELLED"},
  465. { "005", "NO DATA FOUND"}
  466. };
  467. }
  468. }