Example #1
0
        public virtual async Task HandleRefundTransaction(HSOrderWorksheet worksheet, HSPayment creditCardPayment, HSPaymentTransaction creditCardPaymentTransaction, decimal totalToRefund, RMA rma)
        {
            try
            {
                CardConnectRefundRequest requestedRefund = new CardConnectRefundRequest()
                {
                    currency = worksheet.Order.xp.Currency.ToString(),
                    merchid  = creditCardPaymentTransaction.xp.CardConnectResponse.merchid,
                    retref   = creditCardPaymentTransaction.xp.CardConnectResponse.retref,
                    amount   = totalToRefund.ToString("F2"),
                };
                CardConnectRefundResponse response = await _cardConnect.Refund(requestedRefund);

                HSPayment newCreditCardPayment = new HSPayment()
                {
                    Amount = response.amount
                };
                await _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, rma.SourceOrderID, creditCardPayment.ID, CardConnectMapper.Map(newCreditCardPayment, response));
            }
            catch (CreditCardRefundException ex)
            {
                throw new CatalystBaseException(new ApiError
                {
                    ErrorCode = "Payment.FailedToRefund",
                    Message   = ex.ApiError.Message
                });
            }
        }
Example #2
0
        public async Task EmailVoidAuthorizationFailedAsync(HSPayment payment, string transactionID, HSOrder order, CreditCardVoidException ex)
        {
            var templateData = new EmailTemplate <SupportTemplateData>()
            {
                Data = new SupportTemplateData
                {
                    OrderID = order.ID,
                    DynamicPropertyName1  = "BuyerID",
                    DynamicPropertyValue1 = order.FromCompanyID,
                    DynamicPropertyName2  = "Username",
                    DynamicPropertyValue2 = order.FromUser.Username,
                    DynamicPropertyName3  = "PaymentID",
                    DynamicPropertyValue3 = payment.ID,
                    DynamicPropertyName4  = "TransactionID",
                    DynamicPropertyValue4 = transactionID,
                    ErrorJsonString       = JsonConvert.SerializeObject(ex.ApiError)
                },
                Message = new EmailDisplayText()
                {
                    EmailSubject = "Manual intervention required for this order",
                    DynamicText  = "Error encountered while trying to void authorization on this order. Please contact customer and help them manually void authorization"
                }
            };
            var toList        = new List <EmailAddress>();
            var supportEmails = _settings?.SendgridSettings?.CriticalSupportEmails.Split(",");

            foreach (var email in supportEmails)
            {
                toList.Add(new EmailAddress {
                    Email = email
                });
            }
            await SendSingleTemplateEmailMultipleRcpts(_settings?.SendgridSettings?.FromEmail, toList, _settings?.SendgridSettings?.CriticalSupportTemplateID, templateData);
        }
Example #3
0
        private HSPaymentTransaction GetValidTransaction(string orderID, HSPayment payment)
        {
            var ordered     = payment.Transactions.OrderBy(x => x.DateExecuted);
            var transaction = payment.Transactions
                              .OrderBy(x => x.DateExecuted)
                              .LastOrDefault(x => x.Type == "CreditCard" && x.Succeeded);

            if (transaction == null)
            {
                throw new PaymentCaptureJobException("No valid payment authorization on the order", orderID, payment.ID);
            }
            if (transaction?.xp?.CardConnectResponse == null)
            {
                throw new PaymentCaptureJobException("Missing transaction.xp.CardConnectResponse", orderID, payment.ID, transaction.ID);
            }
            var authHasBeenVoided = payment.Transactions.Any(t =>
                                                             t.Type == "CreditCardVoidAuthorization" &&
                                                             t.Succeeded &&
                                                             t.xp?.CardConnectResponse?.retref == transaction.xp?.CardConnectResponse?.retref
                                                             );

            if (authHasBeenVoided)
            {
                throw new PaymentCaptureJobException("Payment authorization has been voided", orderID, payment.ID, transaction.ID);
            }

            return(transaction);
        }
Example #4
0
        public void LogVoidAuthorizationFailed(HSPayment payment, string transactionID, HSOrder order, CreditCardVoidException ex)
        {
            // track in app insights
            // to find go to Transaction Search > Event Type = Event > Filter by any of these custom properties or event name "Payment.VoidAuthorizationFailed"
            var customProperties = new Dictionary <string, string>
            {
                { "Message", "Attempt to void authorization on payment failed" },
                { "OrderID", order.ID },
                { "BuyerID", order.FromCompanyID },
                { "UserEmail", order.FromUser.Email },
                { "PaymentID", payment.ID },
                { "TransactionID", transactionID },
                { "ErrorResponse", JsonConvert.SerializeObject(ex.ApiError, Formatting.Indented) }
            };

            _telemetry.TrackEvent("Payment.VoidAuthorizationFailed", customProperties);
        }
Example #5
0
        private async Task UpdatePoPaymentAsync(HSPayment requestedPayment, HSPayment existingPayment, HSOrderWorksheet worksheet)
        {
            var paymentAmount = worksheet.Order.Total;

            if (existingPayment == null)
            {
                requestedPayment.Amount = paymentAmount;
                await _oc.Payments.CreateAsync <HSPayment>(OrderDirection.Incoming, worksheet.Order.ID, requestedPayment);
            }
            else
            {
                await _oc.Payments.PatchAsync <HSPayment>(OrderDirection.Incoming, worksheet.Order.ID, existingPayment.ID, new PartialPayment
                {
                    Amount = paymentAmount
                });
            }
        }
Example #6
0
        public async Task VoidTransactionAsync(HSPayment payment, HSOrder order, string userToken)
        {
            var transactionID = "";

            try
            {
                if (payment.Accepted == true)
                {
                    var transaction = payment.Transactions
                                      .Where(x => x.Type == "CreditCard")
                                      .OrderBy(x => x.DateExecuted)
                                      .LastOrDefault(t => t.Succeeded);
                    var retref = transaction?.xp?.CardConnectResponse?.retref;
                    if (retref != null)
                    {
                        transactionID = transaction.ID;
                        var userCurrency = await _hsExchangeRates.GetCurrencyForUser(userToken);

                        var response = await _cardConnect.VoidAuthorization(new CardConnectVoidRequest
                        {
                            currency = userCurrency.ToString(),
                            merchid  = GetMerchantID(userCurrency),
                            retref   = transaction.xp.CardConnectResponse.retref
                        });
                        await WithRetry().ExecuteAsync(() => _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, order.ID, payment.ID, CardConnectMapper.Map(payment, response)));
                    }
                }
            }
            catch (CreditCardVoidException ex)
            {
                await _supportAlerts.VoidAuthorizationFailed(payment, transactionID, order, ex);
                await WithRetry().ExecuteAsync(() => _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, order.ID, payment.ID, CardConnectMapper.Map(payment, ex.Response)));

                throw new OrderCloudIntegrationException(new ApiError
                {
                    ErrorCode = "Payment.FailedToVoidAuthorization",
                    Message   = ex.ApiError.Message
                });
            }
        }
Example #7
0
        private async Task UpdateCCPaymentAsync(HSPayment requestedPayment, HSPayment existingPayment, HSOrderWorksheet worksheet, string userToken)
        {
            var paymentAmount = worksheet.Order.Total;

            if (existingPayment == null)
            {
                requestedPayment.Amount   = paymentAmount;
                requestedPayment.Accepted = false;
                requestedPayment.Type     = requestedPayment.Type;
                await _oc.Payments.CreateAsync <HSPayment>(OrderDirection.Outgoing, worksheet.Order.ID, requestedPayment, userToken); // need user token because admins cant see personal credit cards
            }
            else if (existingPayment.CreditCardID == requestedPayment.CreditCardID && existingPayment.Amount == paymentAmount)
            {
                // do nothing, payment doesnt need updating
                return;
            }
            else if (existingPayment.CreditCardID == requestedPayment.CreditCardID)
            {
                await _ccCommand.VoidTransactionAsync(existingPayment, worksheet.Order, userToken);

                await _oc.Payments.PatchAsync <HSPayment>(OrderDirection.Incoming, worksheet.Order.ID, existingPayment.ID, new PartialPayment
                {
                    Accepted = false,
                    Amount   = paymentAmount,
                    xp       = requestedPayment.xp
                });
            }
            else
            {
                // we need to delete payment because you can't have payments totaling more than order total and you can't set payments to $0
                await DeleteCreditCardPaymentAsync(existingPayment, worksheet.Order, userToken);

                requestedPayment.Amount   = paymentAmount;
                requestedPayment.Accepted = false;
                await _oc.Payments.CreateAsync <HSPayment>(OrderDirection.Outgoing, worksheet.Order.ID, requestedPayment, userToken); // need user token because admins cant see personal credit cards
            }
        }
Example #8
0
        private async Task CapturePaymentAsync(HSOrder order, HSPayment payment, HSPaymentTransaction transaction)
        {
            try
            {
                var response = await _cardConnect.Capture(new CardConnectCaptureRequest
                {
                    merchid  = transaction.xp.CardConnectResponse.merchid,
                    retref   = transaction.xp.CardConnectResponse.retref,
                    currency = order.xp.Currency.ToString()
                });

                await _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, order.ID, payment.ID, CardConnectMapper.Map(payment, response));
            }
            catch (CardConnectInquireException ex)
            {
                throw new PaymentCaptureJobException("Error inquiring payment. Message: {ex.ApiError.Message}, ErrorCode: {ex.ApiError.ErrorCode}", order.ID, payment.ID, transaction.ID);
            }
            catch (CardConnectCaptureException ex)
            {
                await _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, order.ID, payment.ID, CardConnectMapper.Map(payment, ex.Response));

                throw new PaymentCaptureJobException($"Error capturing payment. Message: {ex.ApiError.Message}, ErrorCode: {ex.ApiError.ErrorCode}", order.ID, payment.ID, transaction.ID);
            }
        }
Example #9
0
        public virtual async Task HandleRefund(RMA rma, CosmosListPage <RMA> allRMAsOnThisOrder, HSOrderWorksheet worksheet, DecodedToken decodedToken)
        {
            // Get payment info from the order
            ListPage <HSPayment> paymentResponse = await _oc.Payments.ListAsync <HSPayment>(OrderDirection.Incoming, rma.SourceOrderID);

            HSPayment creditCardPayment = paymentResponse.Items.FirstOrDefault(payment => payment.Type == OrderCloud.SDK.PaymentType.CreditCard);

            if (creditCardPayment == null)
            {
                // Items were not paid for with a credit card.  No refund to process via CardConnect.
                return;
            }
            HSPaymentTransaction creditCardPaymentTransaction = creditCardPayment.Transactions
                                                                .OrderBy(x => x.DateExecuted)
                                                                .LastOrDefault(x => x.Type == "CreditCard" && x.Succeeded);
            decimal purchaseOrderTotal = (decimal)paymentResponse.Items
                                         .Where(payment => payment.Type == OrderCloud.SDK.PaymentType.PurchaseOrder)
                                         .Select(payment => payment.Amount)
                                         .Sum();

            // Refund via CardConnect
            CardConnectInquireResponse inquiry = await _cardConnect.Inquire(new CardConnectInquireRequest
            {
                merchid  = creditCardPaymentTransaction.xp.CardConnectResponse.merchid,
                orderid  = rma.SourceOrderID,
                set      = "1",
                currency = worksheet.Order.xp.Currency.ToString(),
                retref   = creditCardPaymentTransaction.xp.CardConnectResponse.retref
            });

            decimal shippingRefund = rma.Type == RMAType.Cancellation ? GetShippingRefundIfCancellingAll(worksheet, rma, allRMAsOnThisOrder) : 0M;

            decimal lineTotalToRefund = rma.LineItems
                                        .Where(li => li.IsResolved &&
                                               !li.IsRefunded &&
                                               li.RefundableViaCreditCard &&
                                               (li.Status == RMALineItemStatus.PartialQtyComplete || li.Status == RMALineItemStatus.Complete)
                                               ).Select(li => li.LineTotalRefund)
                                        .Sum();

            decimal totalToRefund = lineTotalToRefund + shippingRefund;

            // Update Total Credited on RMA
            rma.TotalCredited += totalToRefund;

            // Transactions that are queued for capture can only be fully voided, and we are only allowing partial voids moving forward.
            if (inquiry.voidable == "Y" && inquiry.setlstat == QUEUED_FOR_CAPTURE)
            {
                throw new CatalystBaseException(new ApiError
                {
                    ErrorCode = "Payment.FailedToVoidAuthorization",
                    Message   = "This customer's credit card transaction is currently queued for capture and cannot be refunded at this time.  Please try again later."
                });
            }

            // If voidable, but not refundable, void the refund amount off the original order total
            if (inquiry.voidable == "Y")
            {
                await HandleVoidTransaction(worksheet, creditCardPayment, creditCardPaymentTransaction, totalToRefund, rma);
            }

            // If refundable, but not voidable, do a refund
            if (inquiry.voidable == "N")
            {
                await HandleRefundTransaction(worksheet, creditCardPayment, creditCardPaymentTransaction, totalToRefund, rma);
            }
        }
Example #10
0
 public async Task VoidAuthorizationFailed(HSPayment payment, string transactionID, HSOrder order, CreditCardVoidException ex)
 {
     LogVoidAuthorizationFailed(payment, transactionID, order, ex);
     await _sendgrid.EmailVoidAuthorizationFailedAsync(payment, transactionID, order, ex);
 }
Example #11
0
        private async Task DeleteCreditCardPaymentAsync(HSPayment payment, HSOrder order, string userToken)
        {
            await _ccCommand.VoidTransactionAsync(payment, order, userToken);

            await _oc.Payments.DeleteAsync(OrderDirection.Incoming, order.ID, payment.ID);
        }