/// <summary> /// Constructor /// </summary> /// <param name="payment">The Payment object associated with this request</param> /// <param name="subscriptionPlan">The SubscriptionPlan associated witht this request</param> /// <param name="remoteIP">Remote IP of the user initiating the request</param> public AuthorizeRecurringTransactionRequest(Payment payment, SubscriptionPlan subscriptionPlan, string remoteIP) : base(payment, remoteIP) { this._TransactionOrigin = TransactionOrigin.Internet; if (subscriptionPlan != null) { this.SubscriptionName = subscriptionPlan.Name; this.Amount = payment.Amount; this.RecurringChargeSpecified = subscriptionPlan.RecurringChargeSpecified; // GET THE SUBSCRIPTION CHARGE WITH TAX Order order = payment.Order; int billToProvinceId = ProvinceDataSource.GetProvinceIdByName(order.BillToCountryCode, order.BillToProvince); TaxAddress billingAddress = new TaxAddress(order.BillToCountryCode, billToProvinceId, order.BillToPostalCode); this.RecurringCharge = TaxHelper.GetPriceWithTax(subscriptionPlan.RecurringCharge, subscriptionPlan.TaxCodeId, billingAddress, billingAddress); this.NumberOfPayments = subscriptionPlan.NumberOfPayments; this.PaymentFrequency = subscriptionPlan.PaymentFrequency; this.PaymentFrequencyUnit = subscriptionPlan.PaymentFrequencyUnit; } }
/// <summary> /// Generates payment records for an order /// </summary> /// <param name="order">The order being created</param> /// <param name="checkoutRequest">The checkout request</param> /// <param name="giftCertPayments">The collection of gift certificate payments for this order</param> /// <param name="giftCertPaymentMethodId">The ID of the gift certificate payment method</param> /// <param name="orderItemSubscriptions">Order Item subscriptions</param> internal static void GenerateOrderPayments(Order order, CheckoutRequest checkoutRequest, List <BasketPaymentItem> giftCertPayments, int giftCertPaymentMethodId, Dictionary <int, Subscription[]> orderItemSubscriptions) { //THIS VARIABLE SHOULD ONLY CONTAIN DATA IF POST-CHECKOUT GATEWAY PROCESSING IS REQUIRED string saveAccountData = string.Empty; //USE A COMMON DATE FOR ALL PAYMENTS REGISTERED DateTime paymentDate = LocaleHelper.LocalNow; //CONVERT GIFT CERTIFICATE PLACEHOLDERS INTO PAYMENT ITEMS LSDecimal totalGiftCertPayment = 0; foreach (BasketPaymentItem giftCertItem in giftCertPayments) { Payment giftCertPayment = giftCertItem.GetPaymentObject(); totalGiftCertPayment += giftCertPayment.Amount; giftCertPayment.OrderId = order.OrderId; giftCertPayment.PaymentMethodId = giftCertPaymentMethodId; giftCertPayment.PaymentDate = paymentDate; order.Payments.Add(giftCertPayment); giftCertPayment.Save(); } //IF PAYMENT DATA WAS PASSED WITH CHECKOUT REQUEST, ADD TO ORDER RECORD NOW if (checkoutRequest != null && checkoutRequest.Payment != null) { //BUILD A LIST OF PAYMENTS TO ADD TO THE ORDER BASED ON CONTENTS Payment originalPayment = checkoutRequest.Payment; //DETERMINE TOTAL PAYMENT REQUIRED FOR ITEMS LSDecimal remainingPaymentAmount = order.Items.TotalPrice(); //PRESERVE ACCOUNT DATA saveAccountData = originalPayment.AccountData; //DECIDE WHETHER PAYMENTS MUST BE DIVIDED BECAUSE OF ARB SUBSCRIPTIONS if (orderItemSubscriptions.Count > 0) { //LOOP EACH ORDER ITEM WITH A RECURRING SUBSCRIPTION foreach (int orderItemId in orderItemSubscriptions.Keys) { // THIS STORES THE DISCOUNT TO APPLY TO EACH SUBSCRIPTION PAYMENT LSDecimal subscriptionDiscount = 0; // THIS STORES ADDITIONAL DISCOUNT TO APPLY ONLY TO THE FIRST SUBSCRIPTION LSDecimal firstSubscriptionAdjustment = 0; // GET THE TOTAL DISCOUNTS APPLIED TO THIS ORDER ITEM (VALUE SHOULD BE NEGATIVE) LSDecimal totalItemAdjustments = GetOrderItemAdjustments(order, orderItemId); // IF THERE IS A DISCOUNT, DETERMINE PRO-RATED DISCOUNT FOR EACH PAYMENT (ITEM) if (totalItemAdjustments != 0) { // THERE IS A DISCOUNT THAT APPLIES TO THIS ORDER ITEM, WE NEED TO DETERMINE AMOUNT // SUBSCRIPTION DISCOUNT IS TOTAL DISCOUNT BY THE NUMBER OF SUBSCRIPTION ITEMS subscriptionDiscount = (LSDecimal)Math.Floor((decimal)(totalItemAdjustments / orderItemSubscriptions[orderItemId].Length)); // DETERMINE THE TOTAL DISCOUNT AMOUNT USING CALCULATED PER-SUBSCRIPTION VALUE LSDecimal calculatedTotal = subscriptionDiscount * orderItemSubscriptions[orderItemId].Length; // THE TOTAL DISCOUNT MUST MATCH THE LUMP SUM, SO CALCULATE ANY LEFTOVER DISCOUNT FOR THE FIRST PAYMENT firstSubscriptionAdjustment = (totalItemAdjustments - calculatedTotal); } // WE MUST DETERMINE THE PRICE OF THE ITEM (INCLUDING TAX) // GET THE ORDER ITEM FOR THIS RECURRING SUBSCRIPTION OrderItem orderItem = order.Items[order.Items.IndexOf(orderItemId)]; // GET THE PRICE OF THE ITEM (INCLUDING TAX) int billToProvinceId = ProvinceDataSource.GetProvinceIdByName(order.BillToCountryCode, order.BillToProvince); TaxAddress billingAddress = new TaxAddress(order.BillToCountryCode, billToProvinceId, order.BillToPostalCode); LSDecimal subscriptionPrice = TaxHelper.GetPriceWithTax(orderItem.Price + subscriptionDiscount, orderItem.TaxCodeId, billingAddress, billingAddress); // LOOP ALL THE SUBSCRIPTIONS AND CREATE CORRESPONDING PAYMENT ITEMS foreach (Subscription s in orderItemSubscriptions[orderItemId]) { // ADD PAYMENT ITEM ASSOCIATED WITH THE SUBSCRIPTION Payment arbPayment = new Payment(); arbPayment.SubscriptionId = s.SubscriptionId; arbPayment.Amount = subscriptionPrice + firstSubscriptionAdjustment; arbPayment.CurrencyCode = originalPayment.CurrencyCode; arbPayment.OrderId = order.OrderId; arbPayment.PaymentDate = paymentDate; arbPayment.PaymentMethodId = originalPayment.PaymentMethodId; arbPayment.PaymentMethodName = originalPayment.PaymentMethodName; arbPayment.PaymentStatus = PaymentStatus.Unprocessed; arbPayment.ReferenceNumber = originalPayment.ReferenceNumber; arbPayment.AccountData = saveAccountData; order.Payments.Add(arbPayment); arbPayment.Save(); //ACCOUNT DATA IS RESET AFTER SAVE IN CASE THE SAVE METHOD ALTERS IT BASED //ON MERCHANT SECURITY SETTINGS, WE STILL NEED THE VALUE FOR THE CHECKOUT PROCESS arbPayment.AccountData = saveAccountData; arbPayment.IsDirty = false; // SUBTRACT THIS PAYMENT FROM THE REMAINING TOTAL remainingPaymentAmount -= arbPayment.Amount; // RESET ADJUSTMENT THAT SHOULD APPLY TO FIRST SUBSCRIPTION ONLY firstSubscriptionAdjustment = 0; } } //CREATE AN ADDITIONAL PAYMENT IF THERE IS ANY AMOUNT LEFT TO BE COLLECTED if (remainingPaymentAmount > 0) { //NEED ONE PAYMENT FOR EACH SUBSCRIPTION Payment remainingPayment = new Payment(); remainingPayment.SubscriptionId = 0; if (remainingPaymentAmount >= originalPayment.Amount) { remainingPayment.Amount = originalPayment.Amount; } else { remainingPayment.Amount = remainingPaymentAmount; } remainingPayment.CurrencyCode = originalPayment.CurrencyCode; remainingPayment.OrderId = order.OrderId; remainingPayment.PaymentDate = paymentDate; remainingPayment.PaymentMethodId = originalPayment.PaymentMethodId; remainingPayment.PaymentMethodName = originalPayment.PaymentMethodName; remainingPayment.PaymentStatus = PaymentStatus.Unprocessed; remainingPayment.ReferenceNumber = originalPayment.ReferenceNumber; remainingPayment.AccountData = saveAccountData; order.Payments.Add(remainingPayment); remainingPayment.Save(); //ACCOUNT DATA IS RESET AFTER SAVE IN CASE THE SAVE METHOD ALTERS IT BASED //ON MERCHANT SECURITY SETTINGS, WE STILL NEED THE VALUE FOR THE CHECKOUT PROCESS remainingPayment.AccountData = saveAccountData; remainingPayment.IsDirty = false; } } else { originalPayment.PaymentId = 0; originalPayment.SubscriptionId = 0; originalPayment.PaymentStatus = PaymentStatus.Unprocessed; originalPayment.OrderId = order.OrderId; originalPayment.PaymentDate = paymentDate; order.Payments.Add(originalPayment); originalPayment.Save(); //ACCOUNT DATA IS RESET AFTER SAVE IN CASE THE SAVE METHOD ALTERS IT BASED //ON MERCHANT SECURITY SETTINGS, WE STILL NEED THE VALUE FOR THE CHECKOUT PROCESS originalPayment.AccountData = saveAccountData; originalPayment.IsDirty = false; } } }