public CheckoutResult CheckoutWithPayPalExpressCheckout(Dictionary <string, string> payPalVariables)
        {
            string token   = payPalVariables["token"];
            string payerId = payPalVariables["PayerID"];

            PayPalExpressCheckoutPaymentProvider payPalExpress = new PayPalExpressCheckoutPaymentProvider(storeContext.CurrentStore.GetPaymentProviderConfig(PaymentProviderName.PayPalExpressCheckout));

            int?  orderId      = payPalExpress.GetOrderIdForTransactionToken(token);
            Order pendingOrder = Order.GetOrder(orderId.GetValueOrDefault(-1));

            if (pendingOrder != null)
            {
                PaymentStatusName paymentStatus = payPalExpress.DoExpressCheckoutPayment(pendingOrder, payPalVariables);

                OrderStatusName orderStatus = pendingOrder.OrderStatus;
                if (paymentStatus == PaymentStatusName.Completed)
                {
                    orderStatus = OrderStatusName.Completed;
                    pendingOrder.CreditCardType = CreditCardType.PayPal.ToString();
                }
                UpdateOrderStatus(pendingOrder, orderStatus, paymentStatus);
            }

            return(DoPostCheckoutProcessing(pendingOrder, true));
        }
        internal void UpdateOrderStatus(Order order, OrderStatusName orderStatusName, PaymentStatusName paymentStatusName)
        {
            OrderStatusName   oldOrderStatus   = order.OrderStatus;
            PaymentStatusName oldPaymentStatus = order.PaymentStatus;

            order.PaymentStatus = paymentStatusName;
            order.OrderStatus   = orderStatusName;

            //if (order.PaymentStatus == PaymentStatusName.Completed && !(order.OrderStatus == OrderStatusName.Paid || order.OrderStatus == OrderStatusName.Completed))
            //{
            //    order.OrderStatus = OrderStatusName.Paid;
            //}

            order.Save();

            if (oldPaymentStatus != paymentStatusName)
            {
                //--- Payment Status has changed...
                if (paymentStatusName == PaymentStatusName.Completed && WA.Parser.ToBool(storeContext.CurrentStore.GetSetting(StoreSettingNames.SendPaymentCompleteEmail)).GetValueOrDefault(true))
                {
                    // send "PaymentCompleted" email
                    EmailController             emailController = new EmailController();
                    TokenHelper                 tokenHelper     = new TokenHelper(storeContext);
                    Dictionary <string, string> emailTokens     = tokenHelper.GetOrderTokens(order, true);

                    string emailResponse = emailController.SendEmailTemplate(EmailTemplateNames.PaymentCompleted, emailTokens, order.CustomerEmail, order.UpToStoreByStoreId);
                }
            }
            if (oldOrderStatus != orderStatusName)
            {
                // TODO Order Status has changed... send emails ??
            }
        }
        public CheckoutResult CheckoutWithOnSitePayment(CheckoutOrderInfo checkoutOrderInfo)
        {
            if (!checkoutOrderInfo.RequiresPayment)
            {
                checkoutOrderInfo.PaymentProvider = PaymentProviderName.None;
            }

            CheckoutOrderValidator checkoutOrderValidator = new CheckoutOrderValidator();
            ValidationResult       validationResult       = checkoutOrderValidator.Validate(checkoutOrderInfo);

            if (validationResult.IsValid)
            {
                Order pendingOrder = CreateOrder(checkoutOrderInfo, OrderStatusName.Processing);

                if (checkoutOrderInfo.PaymentProvider != PaymentProviderName.None)
                {
                    PaymentStatusName paymentStatus = pendingOrder.PaymentStatus;
                    if (pendingOrder.PaymentStatus != PaymentStatusName.Completed)
                    {
                        PaymentProvider paymentProcessor = PaymentProviderFactory.GetProvider(checkoutOrderInfo.PaymentProvider, storeContext.CurrentStore);

                        HttpWebResponse response = paymentProcessor.SubmitDirectPaymentRequest(pendingOrder, checkoutOrderInfo.CreditCard);
                        paymentStatus = paymentProcessor.ProcessDirectPaymentResponse(pendingOrder, response);
                    }

                    OrderStatusName orderStatus = (paymentStatus == PaymentStatusName.Completed) ? OrderStatusName.Processing : pendingOrder.OrderStatus;
                    UpdateOrderStatus(pendingOrder, orderStatus, paymentStatus);

                    pendingOrder.Save();
                }
                else
                {
                    // does not require payment (free order / order total == 0)
                    UpdateOrderStatus(pendingOrder, OrderStatusName.Processing, PaymentStatusName.Completed);
                }

                return(DoPostCheckoutProcessing(pendingOrder, true));
            }
            else
            {
                // failed validation
                return(new CheckoutResult()
                {
                    SubmittedOrder = null,
                    Errors = validationResult.Errors.ToList().ConvertAll(e => e.ErrorMessage)
                });
            }
        }
        public CheckoutResult CheckoutWithPayPalStandardCheckout(HttpRequest request)
        {
            Order order = null;
            PayPalStandardProvider payPalStandard = new PayPalStandardProvider(storeContext.CurrentStore.GetPaymentProviderConfig(PaymentProviderName.PayPalStandard));
            int?orderId;

            if (payPalStandard.IsIpnResponse(request, out orderId))
            {
                // user arrived here through a redirect from PayPal Checkout Page
                // so we have "IPN" variables in the Request that we can process
                order = new Order();
                if (order.LoadByPrimaryKey(orderId.GetValueOrDefault(-1)))
                {
                    Exceptions.LogException(new Exception("Loaded order"));
                    PaymentStatusName paymentStatus = payPalStandard.ProcessOffsitePaymentResponse(order, request);
                    Exceptions.LogException(new Exception("paymentStatus:" + paymentStatus));
                    OrderStatusName orderStatus = order.OrderStatus;
                    Exceptions.LogException(new Exception("orderStatus:" + orderStatus));
                    if (paymentStatus == PaymentStatusName.Completed)
                    {
                        orderStatus          = OrderStatusName.Processing;
                        order.CreditCardType = CreditCardType.PayPal.ToString();
                    }
                    if (order.PaymentStatus != paymentStatus || order.OrderStatus != orderStatus)
                    {
                        Exceptions.LogException(new Exception("Updating Order Status"));
                        UpdateOrderStatus(order, orderStatus, paymentStatus);
                        Exceptions.LogException(new Exception("Updated Order Status"));
                        return(DoPostCheckoutProcessing(order, true));
                    }
                    return(DoPostCheckoutProcessing(order, false));
                }
            }
            else
            {
                Exceptions.LogException(new Exception("NOT AN IPN RESPONSE!"));
            }
            return(null);
        }
        public override PaymentStatusName ProcessDirectPaymentResponse(Order order, HttpWebResponse response)
        {
            string responseString = HttpHelper.WebResponseToString(response);

            // Decode the response fields from PayPal API
            string[] encodedPairs = responseString.Split('&');
            Dictionary <string, string> fields = new Dictionary <string, string>(encodedPairs.Length);

            foreach (string field in encodedPairs)
            {
                string[] pair  = field.Split('=');
                string   name  = HttpUtility.UrlDecode(pair[0]);
                string   value = HttpUtility.UrlDecode(pair[1]);

                fields[name] = value;
            }

            // parse response fields into variables
            string    ack     = fields["ACK"];
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            int?    orderId       = WA.Parser.ToInt(fields.TryGetValueOrEmpty("CUSTOM"));
            string  transactionId = fields.TryGetValueOrEmpty("TRANSACTIONID");
            decimal?amount        = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("AMT"));

            string error1Code     = fields.TryGetValueOrEmpty("L_ERRORCODE0");
            string error1ShortMsg = fields.TryGetValueOrEmpty("L_SHORTMESSAGE0");
            string error1LongMsg  = fields.TryGetValueOrEmpty("L_LONGMESSAGE0");
            string error1Severity = fields.TryGetValueOrEmpty("L_SEVERITYCODE0");

            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayDebugResponse = HttpUtility.UrlDecode(responseString);
            newTransaction.Amount = amount;

            PaymentStatusName paymentStatus = order.PaymentStatus;

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                bool paymentAmountMatches = (amount.GetValueOrDefault(-1) == order.Total.Value);
                if (paymentAmountMatches)
                {
                    newTransaction.GatewayResponse = ackType.ToString();
                    paymentStatus = PaymentStatusName.Completed;
                }
                else
                {
                    newTransaction.GatewayResponse = ackType.ToString() + " Amount from Payment Provider does not match the Order amount.";
                    paymentStatus = PaymentStatusName.Pending;
                }

                if (ackType == AckValues.SuccessWithWarning)
                {
                    newTransaction.GatewayError = ackType.ToString() + string.Format(@" ErrorCode: {0}, ShortMsg: {1}, LongMsg: {2}, SeverityCode: {3}", error1Code, error1ShortMsg, error1LongMsg, error1Severity);
                }
            }
            else if (ackType == AckValues.Failure || ackType == AckValues.FailureWithWarning || ackType == AckValues.Warning)
            {
                newTransaction.GatewayError = ackType.ToString() + string.Format(@" ErrorCode: {0}, ShortMsg: {1}, LongMsg: {2}, SeverityCode: {3}", error1Code, error1ShortMsg, error1LongMsg, error1Severity);
                paymentStatus = PaymentStatusName.ProviderError;
            }
            else
            {
                newTransaction.GatewayError = ack + " Invalid / Unknown Response from Payment Provider";
                paymentStatus = PaymentStatusName.ProviderError;
            }

            newTransaction.Save();

            return(paymentStatus);
        }
        // Step 3.
        public PaymentStatusName DoExpressCheckoutPayment(Order order, Dictionary <string, string> payPalVariables)
        {
            string token   = payPalVariables["token"];
            string payerId = payPalVariables["PayerID"];

            Dictionary <string, string> vars = new Dictionary <string, string>();

            vars["METHOD"]  = "DoExpressCheckoutPayment";
            vars["VERSION"] = "60.0";

            vars["USER"]      = ApiUsername;
            vars["PWD"]       = ApiPassword;
            vars["SIGNATURE"] = ApiSignature;

            vars["TOKEN"]   = token;
            vars["PAYERID"] = payerId;

            IEnumerable <OrderItem> orderitems = order.OrderItemCollectionByOrderId;
            int itemNumber = 0;

            foreach (OrderItem orderItem in orderitems)
            {
                string orderItemAttributes = orderItem.GetProductFieldDataPlainTextDisplayString();

                vars["L_NAME" + itemNumber]   = orderItem.Name.Left(127);
                vars["L_DESC" + itemNumber]   = (!string.IsNullOrEmpty(orderItemAttributes) ? " (" + orderItemAttributes + ")" : "").Left(127);
                vars["L_AMT" + itemNumber]    = orderItem.PriceForSingleItem.ToString("N2");
                vars["L_QTY" + itemNumber]    = orderItem.Quantity.Value.ToString("N0");
                vars["L_NUMBER" + itemNumber] = (!string.IsNullOrEmpty(orderItem.Sku) ? " (" + orderItem.Sku + ")" : "");

                itemNumber++;
            }

            vars["PAYMENTACTION"] = "Sale";
            vars["AMT"]           = order.Total.Value.ToString("N2");
            vars["CURRENCYCODE"]  = order.UpToStoreByStoreId.Currency.Code;

            // Send Http POST to PayPal
            HttpWebResponse webResponse    = HttpHelper.HttpPost(ProviderUrl, vars);
            string          responseString = HttpHelper.WebResponseToString(webResponse);

            // Decode the response fields from PayPal API
            Dictionary <string, string> fields = HttpHelper.DecodeVarsFromHttpString(responseString);

            // parse response fields into variables
            string    ack     = fields["ACK"];
            AckValues ackType = WA.Enum <AckValues> .TryParseOrDefault(ack, AckValues.Failure);

            string  correlationId = fields.TryGetValueOrEmpty("CORRELATIONID");
            string  transactionId = fields.TryGetValueOrEmpty("TOKEN");
            decimal?amount        = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("AMT"));
            decimal?taxAmount     = WA.Parser.ToDecimal(fields.TryGetValueOrEmpty("TAXAMT"));
            // TODO - do we need shipping amount too?


            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayResponse      = vars["METHOD"] + ": " + ack;
            newTransaction.GatewayDebugResponse = HttpUtility.UrlDecode(responseString);

            PaymentStatusName paymentStatus = order.PaymentStatus;

            if (ackType == AckValues.Success || ackType == AckValues.SuccessWithWarning)
            {
                bool paymentAmountMatches = (amount.GetValueOrDefault(-1) == order.Total.Value);
                if (paymentAmountMatches)
                {
                    paymentStatus         = PaymentStatusName.Completed;
                    newTransaction.Amount = amount;
                }
                else
                {
                    newTransaction.GatewayError = string.Format("Payment amount does not match Order Total. Order total {0:N2}. Payment amount {1:N2}", order.Total, amount);
                    if (amount >= order.Total)
                    {
                        paymentStatus         = PaymentStatusName.Completed;
                        newTransaction.Amount = amount;
                    }
                    else
                    {
                        paymentStatus = PaymentStatusName.Pending;
                    }
                }
            }
            else
            {
                newTransaction.GatewayError = ack + " Invalid / Unknown Response from Payment Provider";
                paymentStatus = PaymentStatusName.ProviderError;
            }
            newTransaction.Save();

            return(paymentStatus);
        }
 internal void UpdatePaymentStatus(Order order, PaymentStatusName paymentStatus)
 {
     UpdateOrderStatus(order, order.OrderStatus, paymentStatus);
 }
        public override PaymentStatusName ProcessDirectPaymentResponse(Order order, HttpWebResponse response)
        {
            // returned values are returned as a stream, then read into a string
            string responseString = HttpHelper.WebResponseToString(response);

            // the response string is broken into an array
            // The split character specified here must match the delimiting character specified above
            string[] fields = responseString.Split(fieldDelimiterChar);

            ResponseCode responseCode = WA.Enum <ResponseCode> .TryParseOrDefault(fields[0], ResponseCode.Error);

            string  responseSubcode    = fields[1];
            int?    responseReasonCode = WA.Parser.ToInt(fields[2]);
            string  responseReasonText = fields[3]; // could show this to the customer
            string  authorizationCode  = fields[4];
            string  avsResponse        = fields[5];
            string  transactionId      = fields[6];
            int?    orderId            = WA.Parser.ToInt(fields[7]); // "Invoice Number" in Auth.Net docs
            string  description        = fields[8];
            decimal?amount             = WA.Parser.ToDecimal(fields[9]);
            string  method             = fields[10];
            string  transactionType    = fields[11];
            string  customerId         = fields[12];
            //.... fields omitted here....
            string md5Hash          = fields[37];
            string cardCodeResponse = fields[38]; // result of CCV verification
            string cavvResponse     = fields[39]; // Cardholder Authentication Verification Response

            // Custom Merchant-Defined Fields (Pg. 36/37 of Auth.Net Docs)
            if (fields.Length >= 69) // custom fields start at position 69
            {
                // grab any add'l merchant-defined fields here...
            }

            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = order.Id;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayDebugResponse = responseString;
            newTransaction.Amount = amount;

            PaymentStatusName paymentStatus = order.PaymentStatus;

            if (responseCode == ResponseCode.Approved)
            {
                bool paymentAmountMatches = (amount.GetValueOrDefault(-1) == order.Total.Value);

                if (paymentAmountMatches)
                {
                    newTransaction.GatewayResponse = "Approved";
                    paymentStatus = PaymentStatusName.Completed;
                }
                else
                {
                    newTransaction.GatewayResponse = "Payment amount does not match the order amount.";
                    paymentStatus = PaymentStatusName.Pending;
                }
            }
            else if (responseCode == ResponseCode.Declined)
            {
                newTransaction.GatewayError = string.Format("Payment was declined. {1} (Response Reason Code: {0}).", responseReasonCode, responseReasonText);
                paymentStatus = PaymentStatusName.Denied;
            }
            else if (responseCode == ResponseCode.Error)
            {
                newTransaction.GatewayError = string.Format("{1} (Response Reason Code: {0}).", responseReasonCode, responseReasonText);
                paymentStatus = PaymentStatusName.ProviderError;
            }
            else if (responseCode == ResponseCode.HeldForReview)
            {
                newTransaction.GatewayError = string.Format("Payment has been held for review by Authorize.Net. {1} (Response Reason Code: {0}).", responseReasonCode, responseReasonText);
                paymentStatus = PaymentStatusName.Pending;
            }
            else
            {
                newTransaction.GatewayError = "Invalid / Unknown Response from Payment Provider";
                paymentStatus = PaymentStatusName.ProviderError;
            }

            newTransaction.Save();

            return(paymentStatus);
        }
        public override PaymentStatusName ProcessOffsitePaymentResponse(Order order, HttpRequest request)
        {
            //---- Need to validate the incoming request with PayPal so we know it's authentic
            Dictionary <string, string> requestParams = HttpHelper.DecodeParamsFromHttpRequest(request);
            string requestDataString = HttpHelper.EncodeVarsForHttpPostString(requestParams);

            // send our modified request back to PayPal for validation
            string          postData       = requestDataString + "&cmd=_notify-validate";
            HttpWebResponse payPalResponse = HttpHelper.HttpPost(ProviderUrl, postData);

            string payPalResponseString = HttpHelper.WebResponseToString(payPalResponse);

            // Parse response into some variables
            NameValueCollection fields = request.Params;
            string paymentStatus       = fields["payment_status"];
            string transactionId       = fields["txn_id"];
            string receiverEmail       = fields["receiver_email"];
            string businessEmail       = fields["business"];

            int?    orderId              = WA.Parser.ToInt(fields["custom"]);
            decimal?paymentAmount        = WA.Parser.ToDecimal(fields["mc_gross"]); // amount before PayPal fee is subtracted
            decimal?payPalTransactionFee = WA.Parser.ToDecimal(fields["mc_fee"]);
            decimal shippingAmount       = WA.Parser.ToDecimal(fields["mc_shipping"]).GetValueOrDefault(0);
            decimal handlingAmount       = WA.Parser.ToDecimal(fields["mc_handling"]).GetValueOrDefault(0);
            decimal taxAmount            = WA.Parser.ToDecimal(fields["tax"]).GetValueOrDefault(0);

            string firstName = fields["first_name"] ?? "";
            string lastName  = fields["last_name"] ?? "";
            string email     = fields["payer_email"] ?? "";
            string phone     = fields["contact_phone"] ?? "";

            string shipName        = fields["address_name"] ?? "";
            string shipAddress1    = fields["address_street"] ?? "";
            string shipCity        = fields["address_city"] ?? "";
            string shipRegion      = fields["address_state"] ?? "";
            string shipPostalCode  = fields["address_zip"] ?? "";
            string shipCountryCode = fields["address_country_code"] ?? "";

            PaymentStatusName paymentStatusName = order.PaymentStatus;
            bool isValidReceiverEmail           = (receiverEmail == EmailAddress) || (businessEmail == EmailAddress);

            PaymentTransaction newTransaction = new PaymentTransaction();

            newTransaction.OrderId              = orderId;
            newTransaction.PaymentProviderId    = this.ProviderId;
            newTransaction.GatewayUrl           = ProviderUrl;
            newTransaction.GatewayTransactionId = transactionId;
            newTransaction.GatewayResponse      = paymentStatus;
            newTransaction.GatewayDebugResponse = requestDataString;
            newTransaction.Amount = paymentAmount;

            if (payPalResponseString != "VERIFIED")
            {
                newTransaction.GatewayError = "Invalid/Unknown response or response could not be validated as authentic";
                newTransaction.Save();

                return(PaymentStatusName.ProviderError);
            }
            if (!isValidReceiverEmail)
            {
                newTransaction.GatewayError = "Receiver Email does not match PayPal Account";
                newTransaction.Save();

                return(PaymentStatusName.ProviderError);
            }

            //Order pendingOrder = Order.GetOrder(orderId);
            if (payPalResponseString == "VERIFIED")
            {
                // check if we have already processed this transaction successfully
                // we might already have processed the IPN callback, and the user clicks "return to store"
                // which would trigger another transaction
                PaymentTransaction priorTransaction = PaymentTransaction.GetMostRecentByTransactionId(transactionId);
                if (priorTransaction != null && priorTransaction.OrderId == orderId)
                {
                    // we have a duplicate transaction, we should NOT process this new one
                    return(order.PaymentStatus);
                }

                //check the payment_status is Completed
                bool paymentStatusComplete = (paymentStatus.ToLower() == "completed");

                if (paymentAmount.GetValueOrDefault(-1) < order.Total.Value)
                {
                    newTransaction.GatewayError = string.Format("Payment amount does not match Order Total. Order total {0:N2}. Payment amount {1:N2}", order.Total, paymentAmount);
                    newTransaction.Save();
                    return(PaymentStatusName.ProviderError);
                }

                //if (paymentStatusComplete && paymentAmountMatches && receiverEmailMatches)
                if (paymentStatusComplete)
                {
                    newTransaction.GatewayResponse = "Verified Payment Completed";
                    if (request.RawUrl.Contains("PayPalIpnHandler.aspx"))
                    {
                        newTransaction.GatewayResponse += " via IPN";
                    }
                    else if (request.RawUrl.Contains("Checkout-Complete.aspx"))
                    {
                        newTransaction.GatewayResponse += " via return button from PayPal";
                    }
                    paymentStatusName = PaymentStatusName.Completed;

                    //---- Update the Order
                    order.CustomerFirstName = firstName;
                    order.CustomerLastName  = lastName;
                    order.CustomerEmail     = email;

                    order.ShipRecipientName = shipName;
                    order.ShipAddress1      = shipAddress1;
                    //order.ShipAddress2 = shipAddress2;
                    order.ShipCity        = shipCity;
                    order.ShipRegion      = shipRegion;
                    order.ShipPostalCode  = shipPostalCode;
                    order.ShipCountryCode = shipCountryCode;
                    order.ShipTelephone   = phone;

                    order.BillAddress1 = shipAddress1;
                    //order.BillAddress2 = shipAddress2;
                    order.BillCity        = shipCity;
                    order.BillRegion      = shipRegion;
                    order.BillPostalCode  = shipPostalCode;
                    order.BillCountryCode = shipCountryCode;
                    order.BillTelephone   = phone;

                    // PayPal 'paymentAmount' is the total amount paid by customer
                    // so we need to do some basic math to get the other order amounts...
                    order.ShippingAmount = shippingAmount + handlingAmount;
                    order.TaxAmount      = taxAmount;
                    order.SubTotal       = paymentAmount - (order.ShippingAmount + order.TaxAmount);
                    order.Total          = paymentAmount;

                    order.Save();
                }
                newTransaction.Save();
            }

            return(paymentStatusName);
        }