/// <summary> /// Where possible, performs the conversion between supplied currency and the other currency a business is set up for. /// </summary> /// <param name="payment">The payment to convert</param> private void SetPaymentConvertedValue(Payment payment, long businessId) { if (businessId == 0) { var order = OrderManager.GetOrderWithBookingsByKey(payment.OrderId); var booking = order.Bookings.FirstOrDefault(); if (booking != null) { businessId = booking.BusinessId; } } if (businessId > 0) { var business = Cache.Cache.Business.GetValue(businessId); if (!business.WorkingCurrencyCode.Equals(business.ContractCurrencyCode)) { try { if (business.WorkingCurrencyCode.Equals(payment.Currency.ISOCode)) { payment.ConvertedAmount = FxManager.ConvertMoney(new Money(payment.Amount, payment.Currency.ISOCode), business.ContractCurrencyCode, business.Id); } else if (business.ContractCurrencyCode.Equals(payment.Currency.ISOCode)) { payment.ConvertedAmount = FxManager.ConvertMoney(new Money(payment.Amount, payment.Currency.ISOCode), business.WorkingCurrencyCode, business.Id); } } catch (Exception ex) { Logger.LogError("Conversion failed for {0} Payment on Business {1}", ex: ex, args: new[] {payment.Currency.ISOCode, businessId.ToString()}); payment.ConvertedAmount = null; } } } }
/// <summary> /// Add a queueItem based on the payment and invoice request /// </summary> /// <param name="payment">payment information</param> /// <param name="payload">invoice request</param> /// <returns>queue item with id set</returns> virtual internal QueueItem GetSettlementQueueItemFromPayment(Payment payment, SettlementInvoiceRequest payload) { QueueItem queItem = null; if (payload.IsValid()) { // create queue item from payment sent in queItem = new QueueItem { Key = payment.OrderId.ToString(), PayloadType = PayloadTypeEnum.BookingInv, QueueCode = QueueCodeType.SettlementInvoiceRequest, UserId = payment.CreatedByUserId, Payload = XmlSerialization.XmlSerializeToString(payload) }; } return queItem; }
/// <summary> /// Centralises the logic to create payments and events /// </summary> /// <param name="payment">Payment</param> /// <param name="orderEventType">Order event type to create</param> /// <param name="paymentEventType">Payment event type to create</param> private void CreatePayment(Payment payment, OrderEventTypeEnum orderEventType, PaymentEventTypeEnum paymentEventType, long businessId) { //input validation if (payment.IsValid()) { SetPaymentConvertedValue(payment, businessId); using (var tx = new BusinessTransaction()) { //enforce that the values are positive and negative according to the type of payment we're trying to create if ((payment.Amount > 0 && payment.PaymentTypeEnum == PaymentTypeEnum.Refund) || (payment.Amount < 0 && payment.PaymentTypeEnum == PaymentTypeEnum.Payment)) { payment.Amount *= -1; } //Set Merchant Type if not already done payment.MerchantType = payment.MerchantType ?? paymentDao.GetMerchantType(payment.OrderId); //create payment paymentDao.Create(payment); //create the payment event eventTrackingManager.CreatePaymentEvent(payment.Id, paymentEventType, payment.Notes); //create the order event eventTrackingManager.CreateOrderEvent(payment.OrderId, orderEventType, payment.Id.ToString(), payment.Notes); tx.Commit(); } } }
/// <summary> /// Sets up payment for OTA (mostly expedia) prepay cases /// </summary> /// <param name="order">order for the booking</param> /// <param name="business">business of booking</param> /// <param name="receivedDate">date to set for payment</param> /// <returns>Filled in Payment</returns> public Payment SetupOtaPayment(Model.Order.Order order, Model.Business.Business business, DateTime receivedDate) { Payment payment = null; foreach (var booking in order.Bookings) { // add payment if ota booking, amount paid == 0, we have a cost, and it is on account booking if (order.IntegrationType.HasValue && (order.IntegrationType.Value == IntegrationTypeEnum.Push || order.IntegrationType.Value == IntegrationTypeEnum.RequestResponse) && booking.Cost.HasValue && (!booking.AmountPaid.HasValue || booking.AmountPaid.Value == 0) && (booking.BookingScenarioType == BookingScenarioTypeEnum.OnAccountBooking || booking.BookingScenario == BookingScenarioTypeEnum.OnAccountBooking.GetCode())) { string translatedNote = dictionaryDao.GetItemByKey(PAYMENT_NOTE_DICT_KEY, business.DefaultCultureCode); if (string.IsNullOrWhiteSpace(translatedNote)) { // use default translatedNote = PAYMENT_NOTE; } if (payment == null) { payment = new Payment { PaymentMethod = new PaymentMethod {Code = PaymentMethodEnum.OtaPrePay.GetCode()}, PaymentMethodEnum = PaymentMethodEnum.OtaPrePay, Amount = booking.Cost.Value, PaymentSource = new PaymentSource {Code = PaymentSourceEnum.Online.GetCode()}, PaymentSourceEnum = PaymentSourceEnum.Online, PaymentType = new PaymentType {Code = PaymentTypeEnum.Payment.GetCode()}, PaymentTypeEnum = PaymentTypeEnum.Payment, ReceivedDate = receivedDate, PaymentStatus = new PaymentStatus {Code = PaymentStatusEnum.Created.GetCode()}, PaymentStatusEnum = PaymentStatusEnum.Created, OrderId = booking.OrderId, Notes = string.Format(translatedNote, order.Channel.Name) }; } else { payment.Amount += booking.Cost.Value; } } } return payment; }
/// <summary> /// Create the eagle payment and queue item for settlement after payment through service /// </summary> /// <param name="result">result of payment service</param> /// <param name="order">order paid to with bookings</param> /// <param name="note">note for payment</param> /// <returns>true if successful</returns> internal bool CreatePaymentAndQueueToSettlement(PreAuthPaymentResult result, Model.Order.Order order, string note) { if (result.Success) { if (order != null) { Model.Booking.Booking firstBooking = order.Bookings.FirstOrDefault(); var paymentType = result.Amount > 0 ? PaymentTypeEnum.Payment : PaymentTypeEnum.Refund; Payment payment = new Payment { Amount = result.Amount, OrderId = order.Id.Value, CreatedByUserId = order.UpdatedByUserId, PaymentMethodEnum = PaymentMethodEnum.CreditCard, // assume credit card for now PaymentMethod = new PaymentMethod { Code = PaymentMethodEnum.CreditCard.GetCode() }, CardLast4Digits = result.LastCardDigits, PaymentSource = new PaymentSource { Code = PaymentSourceEnum.Online.GetCode() }, PaymentSourceEnum = PaymentSourceEnum.Online, PaymentType = new PaymentType { Code = paymentType.GetCode() }, PaymentTypeEnum = paymentType, PaymentStatus = new PaymentStatus { Code = PaymentStatusEnum.Created.GetCode() }, PaymentStatusEnum = PaymentStatusEnum.Created, Currency = new Model.Common.Currency(order.CustomerCurrencyCode, string.Empty, string.Empty, 2), ReceivedDate = result.TransactionDate, MerchantType = null, // needs to be null in order to be filled in when being created Notes = note, TransactionSequence = result.Sequence }; CardTypeEnum cardType = CardType.ConvertOgoneCardTypeToEagleCardType(result.CardTypeCode); payment.CardType = cardType != CardTypeEnum.Unknown ? new CardType {Code = cardType.GetCode()} : null; // add this payment to the order CreatePaymentForOrder(payment, firstBooking.BusinessId); SettlementInvoiceRequest invoiceRequest = new SettlementInvoiceRequest { OrderSourceCode = order.OrderSourceCode, BusinessId = firstBooking.BusinessId, BookingId = firstBooking.Id.Value, OrderId = order.Id.Value }; QueueItem queItem = GetSettlementQueueItemFromPayment(payment, invoiceRequest); if (queItem == null || queueManager.AddQueueItem(queItem) == null) { // failed to create the queue item throw new ValidationException(ErrorFactory.CreateAndLogError(Errors.SRVEX30139, "PaymentManager.ChargePreAuth", additionalDescriptionParameters: new object[] { order.OrderReference }, arguments: new object[] { order.OrderReference })); } return true; } } return false; }
/// <summary> /// Delete Payment /// </summary> /// <param name="payment">Payment to delete</param> /// <returns><c>True</c> if deleted, <c>False</c> if not</returns> public bool DeletePayment(Payment payment) { using (var tx = new BusinessTransaction()) { //Modify payment and mark it as deleted payment.PaymentStatusEnum = PaymentStatusEnum.Deleted; paymentDao.Modify(payment); //record event for the deletion of the payment eventTrackingManager.CreatePaymentEvent(payment.Id, PaymentEventTypeEnum.Deleted, payment.Notes); //create the order event eventTrackingManager.CreateOrderEvent(payment.OrderId, OrderEventTypeEnum.PaymentDeleted, payment.Id.ToString(), payment.Notes); tx.Commit(); } return true; }
/// <summary> /// Create Online Refund For booking /// </summary> /// <param name="payment">Payment to create</param> /// <param name="isSuccess">If the payment with the payment gateway was successful or not</param> public void CreateOnlineRefundForOrder(Payment payment, bool isSuccess, long businessId) { if (payment == null) { return; } var paymentEventType = isSuccess ? PaymentEventTypeEnum.OnlinePaymentRefund : PaymentEventTypeEnum.OnlinePaymentRefundFail; payment.PaymentTypeEnum = PaymentTypeEnum.Refund; CreatePayment(payment, OrderEventTypeEnum.PaymentAdded, paymentEventType, businessId); }
/// <summary> /// Creates a payment for a particular order /// </summary> /// <param name="payment">Payment object</param> public virtual void CreatePaymentForOrder(Payment payment, long businessId) { CreatePayment(payment, OrderEventTypeEnum.PaymentAdded, PaymentEventTypeEnum.Created, businessId); }
/// <summary> /// Modify Payment /// </summary> /// This needs to support updating the PaymentConversion table *if* we ever use it for any operation other than 'Delete'. /// <param name="payment">Payment to modify</param> /// <returns><c>True</c> if modified, <c>false</c> if not</returns> public bool Modify(Payment payment) { const string SQL_STATEMENT = @" UPDATE Booking.Payment SET OrderId = @OrderId, PaymentSourceCode = @PaymentSourceCode, PaymentTypeCode = @PaymentTypeCode, PaymentMethodCode = @PaymentMethodCode, CurrencyCode = @CurrencyCode, Amount = @Amount, PaymentName = @PaymentName, CardTypeCode = @CardTypeCode, CardLast4Digits = @CardLast4Digits, Notes = @Notes, PaymentStatusCode = @PaymentStatusCode, ReceivedDate = @ReceivedDate, UpdatedByUserId = @UpdatedByUserId WHERE Id = @Id"; var parameters = new List<SqlParameter> { DbHelper.CreateParameter(PaymentMapper.Parameters.Id, payment.Id), DbHelper.CreateParameter(PaymentMapper.Parameters.OrderId, payment.OrderId), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentSourceCode, payment.PaymentSourceEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentTypeCode, payment.PaymentTypeEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentMethodCode, payment.PaymentMethodEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.CurrencyCode, payment.Currency.ISOCode), DbHelper.CreateParameter(PaymentMapper.Parameters.Amount, payment.Amount), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentName, payment.PaymentName), DbHelper.CreateParameter(PaymentMapper.Parameters.CardTypeCode, payment.CardType.Code), DbHelper.CreateParameter(PaymentMapper.Parameters.CardLast4Digits, payment.CardLast4Digits), DbHelper.CreateParameter(PaymentMapper.Parameters.Notes, payment.Notes), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentStatusCode, payment.PaymentStatusEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.ReceivedDate, payment.ReceivedDate) }; // Add auditing parameters AuditFieldsHelper.PopulateAuditFields(parameters); int rowsAffected = DbHelper.ExecuteNonQueryCommand(SQL_STATEMENT, parameters: parameters); // Check if the update was successful if (rowsAffected == 0) { throw new ExpectedResultException(ErrorFactory.CreateAndLogError(Errors.SRVEX30027, "PaymentDao.Modify", additionalDescriptionParameters: (new object[] { payment.GetType().Name, payment.Id }), arguments: new object[] { payment.GetType().Name, payment.Id })); } return true; }
/// <summary> /// Create a payment /// </summary> /// <param name="payment">Payment object</param> public void Create(Payment payment) { const string SQL_STATEMENT = @" INSERT INTO Booking.Payment ( OrderId, PaymentSourceCode, PaymentTypeCode, PaymentMethodCode, CurrencyCode, Amount, PaymentName, CardTypeCode, CardLast4Digits, Notes, PaymentStatusCode, ReceivedDate, UpdatedByUserId, MerchantTypeCode, TransactionSequence ) VALUES ( @OrderId, @PaymentSourceCode, @PaymentTypeCode, @PaymentMethodCode, @CurrencyCode, @Amount, @PaymentName, @CardTypeCode, @CardLast4Digits, @Notes, @PaymentStatusCode, @ReceivedDate, @UpdatedByUserId, @MerchantTypeCode, @TransactionSequence ) SELECT @Id = SCOPE_IDENTITY()"; const string SQL_OPTIONAL_STATEMENT = @" INSERT INTO Booking.PaymentConversion ( PaymentId, Currency, Amount ) VALUES ( @Id, @ConvertedCurrency, @ConvertedAmount )"; //make sure Order is valid const string SQL_STATEMENT_VALIDATION = @" SELECT COUNT(Id) FROM Booking.Orders WHERE Id = @Id"; //create parameters to validate that the order is valid var validationParameters = new List<SqlParameter> { DbHelper.CreateParameter(PaymentMapper.Parameters.Id, payment.OrderId) }; //check for existence of order if invalid then throw an exception var rowCount = DbHelper.ExecuteScalar<int>(SQL_STATEMENT_VALIDATION, parameters: validationParameters); if (rowCount == default(int)) { throw new ExpectedResultException(ErrorFactory.CreateAndLogError(Errors.SRVEX30073, "PaymentDao.Create", additionalDescriptionParameters: (new object[] { payment.OrderId }), arguments: new object[] { payment.OrderId })); } //create the parameters var parameters = new List<SqlParameter> { DbHelper.CreateParameter(PaymentMapper.Parameters.OrderId, payment.OrderId), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentSourceCode, payment.PaymentSourceEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentTypeCode, payment.PaymentTypeEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentMethodCode, payment.PaymentMethodEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.CurrencyCode, payment.Currency.ISOCode), DbHelper.CreateParameter(PaymentMapper.Parameters.Amount, payment.Amount), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentName, payment.PaymentName), DbHelper.CreateParameter(PaymentMapper.Parameters.CardTypeCode, payment.CardType != null ? payment.CardType.Code : null), DbHelper.CreateParameter(PaymentMapper.Parameters.CardLast4Digits, payment.CardLast4Digits), DbHelper.CreateParameter(PaymentMapper.Parameters.Notes, payment.Notes), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentStatusCode, payment.PaymentStatusEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.ReceivedDate, payment.ReceivedDate), DbHelper.CreateParameter(PaymentMapper.Parameters.PaymentType, payment.PaymentTypeEnum.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.MerchantTypeCode, payment.MerchantType.GetCode()), DbHelper.CreateParameter(PaymentMapper.Parameters.TransactionSequence, payment.TransactionSequence) }; var sql = SQL_STATEMENT; if (payment.ConvertedAmount != null) { sql = sql + SQL_OPTIONAL_STATEMENT; parameters.Add(DbHelper.CreateParameter(PaymentMapper.Parameters.ConvertedCurrency, payment.ConvertedAmount.Currency)); parameters.Add(DbHelper.CreateParameter(PaymentMapper.Parameters.ConvertedAmount, payment.ConvertedAmount.Amount)); } SqlParameter outputKey; parameters.Add(outputKey = DbHelper.CreateParameterOut<int>(PaymentMapper.Parameters.Id, SqlDbType.Int)); // Add auditing parameters AuditFieldsHelper.PopulateAuditFields(parameters); //Execute the SQL statement DbHelper.ExecuteNonQueryCommand(sql, parameters: parameters); payment.Id = DbHelper.ParameterValue<int>(outputKey); }