///<summary>Creates and returns the HPF URL and validation OTK which can be used to make a payment for an unspecified credit card. Throws exceptions.</summary> public static string GetHpfUrlForPayment(Patient pat, string accountToken, string payNote, bool isMobile, double amount, bool saveToken, CreditCardSource ccSource) { if (pat == null) { throw new ODException("No Patient Found", ODException.ErrorCodes.NoPatientFound); } if (string.IsNullOrWhiteSpace(accountToken)) { throw new ODException("Invalid Account Token", ODException.ErrorCodes.OtkArgsInvalid); } if (amount < 0.00 || amount > 99999.99) { throw new ODException("Invalid Amount", ODException.ErrorCodes.OtkArgsInvalid); } if (string.IsNullOrEmpty(payNote)) { throw new ODException("Invalid PayNote", ODException.ErrorCodes.OtkArgsInvalid); } PayConnectResponseWeb responseWeb = new PayConnectResponseWeb() { Amount = amount, AccountToken = accountToken, PatNum = pat.PatNum, ProcessingStatus = PayConnectWebStatus.Created, PayNote = payNote, CCSource = ccSource, IsTokenSaved = saveToken, }; PayConnectResponseWebs.Insert(responseWeb); try { string url; PayConnectREST.PostPaymentRequest(responseWeb, out url); PayConnectResponseWebs.Update(responseWeb); WakeupWebPaymentsMonitor?.Invoke(url, new EventArgs()); return(url); } catch (Exception e) { PayConnectResponseWebs.HandleResponseError(responseWeb, "Error calling PostPaymentRequest: " + e.Message); PayConnectResponseWebs.Update(responseWeb); throw; } }
///<summary>Returns number of pending transactions remaining after completion.</summary> private static int ProcessOutstandingTransactions() { OnLoggerEvent("Checking for outstanding PaymentTokens."); List <PayConnectResponseWeb> listPendingPaymentsAll = PayConnectResponseWebs.GetAllPending(); //Only process if it's been >= 5 seconds. List <PayConnectResponseWeb> listPendingDue = listPendingPaymentsAll.FindAll(x => DateTime.Now.Subtract(x.GetLastPendingUpdateDateTime()) > TimeSpan.FromSeconds(5)); OnLoggerEvent("Found " + listPendingPaymentsAll.Count + " PaymentTokens. " + listPendingDue.Count.ToString() + " are due to be processed."); if (listPendingDue.Count <= 0) //None are due this time around but we may have some still pending, return count of those. { return(listPendingPaymentsAll.Count); } OnLoggerEvent("Processing " + listPendingDue.Count.ToString() + " outstanding PaymentTokens.", LogLevel.Information); //Seed total remaining with any that we won't be processing this time around. int remaining = listPendingPaymentsAll.Count - listPendingDue.Count; foreach (PayConnectResponseWeb responseWebCur in listPendingDue) { try { //This method will update the responseWebCur with all of the data from the /paymentStatus API call PayConnectREST.GetPaymentStatus(responseWebCur); switch (responseWebCur.ProcessingStatus) { case PayConnectWebStatus.Pending: //No new status to report. Try again next time. OnLoggerEvent("PaymentToken still pending: " + responseWebCur.PayToken, LogLevel.Information); if (DateTime.Now.AddMinutes(30) < responseWebCur.GetLastPendingUpdateDateTime()) { //Expire this transaction ourselves after 30 minutes. In testing, PayConnect expires them after 15 minutes. responseWebCur.ProcessingStatus = PayConnectWebStatus.Expired; responseWebCur.DateTimeExpired = MiscData.GetNowDateTime(); OnLoggerEvent("PaymentToken has expired: " + responseWebCur.PayToken, LogLevel.Information); } break; case PayConnectWebStatus.CreatedError: case PayConnectWebStatus.PendingError: OnLoggerEvent("PaymentToken returned an error when retreiving a status update: " + responseWebCur.PayToken, LogLevel.Information); break; case PayConnectWebStatus.Expired: OnLoggerEvent("PaymentToken has expired: " + responseWebCur.PayToken, LogLevel.Information); break; case PayConnectWebStatus.Completed: OnLoggerEvent("PaymentToken has been completed: " + responseWebCur.PayToken, LogLevel.Information); if (responseWebCur.IsTokenSaved) { CreditCards.InsertFromPayConnect(responseWebCur); } Patient pat = Patients.GetPat(responseWebCur.PatNum); long clinicNum = 0; if (PrefC.HasClinicsEnabled) { clinicNum = pat.ClinicNum; } string receipt = ""; if (!string.IsNullOrWhiteSpace(responseWebCur.LastResponseStr)) { var pcInfo = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(responseWebCur.LastResponseStr, new { Amount = (decimal)0.0, CreditCardNumber = "", Messages = new { Message = new string[0] }, RefNumber = "", Status = new { description = "" }, } ); receipt = BuildReceiptString(PayConnectService.transType.SALE, pcInfo.RefNumber, "", pcInfo.CreditCardNumber, "", "", pcInfo.Status?.description ?? "", pcInfo.Messages.Message.ToList(), pcInfo.Amount, 0, clinicNum); } responseWebCur.PayNum = Payments.InsertFromPayConnect(pat.PatNum, pat.PriProv, clinicNum, responseWebCur.Amount, responseWebCur.GetFormattedNote(true), receipt, responseWebCur.CCSource); break; case PayConnectWebStatus.Cancelled: OnLoggerEvent("PaymentToken has been cancelled: " + responseWebCur.PayToken, LogLevel.Information); break; case PayConnectWebStatus.Declined: OnLoggerEvent("PaymentToken has been declined: " + responseWebCur.PayToken, LogLevel.Information); break; case PayConnectWebStatus.Created: case PayConnectWebStatus.Unknown: case PayConnectWebStatus.UnknownError: default: OnLoggerEvent($"PaymentToken {responseWebCur.PayToken} returned unsupported state: {responseWebCur.ProcessingStatus.ToString()}", LogLevel.Information); break; } } catch (Exception e) { e.DoNothing(); } finally { PayConnectResponseWebs.Update(responseWebCur); } } remaining = listPendingPaymentsAll.FindAll(x => x.ProcessingStatus == PayConnectWebStatus.Pending).Count; OnLoggerEvent(remaining.ToString() + " PaymentTokens still pending after processing.", LogLevel.Information); return(remaining); }