/// <summary> /// Processes the Authorize and AuthorizeAndCapture transactions /// </summary> /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param> /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param> /// <param name="args"></param> /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns> public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, CreditCard creditCard) { try { #region New //Credit Card Information var models = Map(invoice, payment, creditCard); var response = CardConnectService.AuthorizeTransaction(models); #endregion New if (response != null) { //Get the CardConnect Response switch (response.Respstat) { case "A": { payment.Collected = false; payment.Authorized = true; payment.SaveCarConnectTransactionRecord(response); GatewayProviderService service = new GatewayProviderService(); service.ApplyPaymentToInvoice( payment.Key, invoice.Key, Core.AppliedPaymentType.Debit, "CardConnect: " + response.Resptext + " Authorized Amount " + string.Format("{0:C}", invoice.Total) + " for Capture... RetRef: " + response.Retref, 0 ); //If the payment was acepted, redirect the user to a thank you landing page return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); } case "B": case "C": default: { payment.Collected = false; payment.Authorized = false; //If the payment was'nt acepted, redirect the user to a Cancel Url return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception("CardConnect: " + response.Resptext)), invoice, true)); } } } else { payment.Collected = false; payment.Authorized = false; return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception("CardConnect: Null Response")), invoice, true)); } } catch (Exception ex) { payment.Collected = false; payment.Authorized = false; return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, true)); } }
public IPaymentResult CapturePayment(IInvoice invoice, IPayment payment, decimal amount, bool isPartialPayment) { try { payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentCaptured, "true"); payment.Collected = true; } catch (Exception ex) { return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, false)); } return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); }
/// <summary> /// Create a dictionary with credentials for SagePay service. /// </summary> /// <param name="settings"></param> /// <returns></returns> //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings) //{ // return new Dictionary<string, string> // { // {"mode", GetModeString(settings.LiveMode)}, // {"account.vendorName", settings.VendorName}, // {"account.encyptionPassword", settings.EncyptionPassword}, // {"account.apiVersion", settings.ApiVersion} // }; //} public IPaymentResult AuthorizePayment(IInvoice invoice, IPayment payment) { try { payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentAuthorized, "true"); payment.Authorized = true; } catch (Exception ex) { return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, false)); } return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); }
public IPaymentResult CapturePayment(IInvoice invoice, IPayment payment, decimal amount, bool isPartialPayment) { try { payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentCaptured, "true"); payment.Collected = true; } catch (Exception ex) { return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false); } return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); }
/// <summary> /// Create a dictionary with credentials for SagePay service. /// </summary> /// <param name="settings"></param> /// <returns></returns> //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings) //{ // return new Dictionary<string, string> // { // {"mode", GetModeString(settings.LiveMode)}, // {"account.vendorName", settings.VendorName}, // {"account.encyptionPassword", settings.EncyptionPassword}, // {"account.apiVersion", settings.ApiVersion} // }; //} public IPaymentResult AuthorizePayment(IInvoice invoice, IPayment payment) { try { payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.PaymentAuthorized, "true"); payment.Authorized = true; } catch (Exception ex) { return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false); } return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); }
private CardConnectAuthorizationRequestModel Map(IInvoice invoice, IPayment payment, CreditCard creditCard) { return(new CardConnectAuthorizationRequestModel { Merchid = MerchId, Accttype = creditCard.CreditCardType, Account = creditCard.CardNumber, Name = creditCard.CardholderName, Expiry = creditCard.ExpireMonth + creditCard.ExpireYear, Cvv2 = creditCard.CardCode, Amount = payment.Amount, Currency = invoice.CurrencyCode, Orderid = invoice.InvoiceNumber, Address = invoice.BillToAddress1, City = invoice.BillToLocality, Region = invoice.BillToRegion, Country = invoice.BillToCountryCode, Postal = invoice.BillToPostalCode, Tokenize = "Y", Capture = "Y" }); }
/// <summary> /// Create a dictionary with credentials for SagePay service. /// </summary> /// <param name="settings"></param> /// <returns></returns> //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings) //{ // return new Dictionary<string, string> // { // {"mode", GetModeString(settings.LiveMode)}, // {"account.vendorName", settings.VendorName}, // {"account.encyptionPassword", settings.EncyptionPassword}, // {"account.apiVersion", settings.ApiVersion} // }; //} /// <summary> /// Processes the Authorize and AuthorizeAndCapture transactions /// </summary> /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param> /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param> /// <param name="args"></param> /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns> public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { try { // Gather SagePay settings info and formulate it into a Dictionary for posting to the gateway var sagePayFormIntegration = new SagePayFormIntegration(Settings); var request = sagePayFormIntegration.FormPaymentRequest(); SetSagePayApiData(request, invoice, payment); // Do basic validation as per the SagePay kit var errors = sagePayFormIntegration.Validation(request); if (errors.Count > 0) { return(new PaymentResult(Attempt <IPayment> .Fail(payment, base.CreateErrorResult(errors)), invoice, true)); } // Use the SagePay methods to encrypt the form so it can be posted sagePayFormIntegration.ProcessRequest(request); // Prepare a HTTP post var content = new FormUrlEncodedContent( new Dictionary <string, string> { { "VPSProtocol", Settings.ApiVersion }, { "TxType", Constants.TransactionType }, { "Vendor", Settings.VendorName }, //{"Profile", "LOW"}, // Provider setting? { "Crypt", request.Crypt } }); try { // Post the form to SagePay VSP var formPaymentUrl = string.Format("https://{0}.sagepay.com/gateway/service/vspform-register.vsp", Settings.Environment); // TEST form post //var formPaymentUrl = string.Format("https://{0}.sagepay.com/showpost/showpost.asp", GetModeString(Settings.LiveMode)); var result = new HttpClient().PostAsync(formPaymentUrl, content).Result; // Store transaction details in ExtendedData payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.VendorTransactionCode, request.VendorTxCode); // Flag an error in the backoffice if the result is not successful if (!result.IsSuccessStatusCode) { return(new PaymentResult(Attempt <IPayment> .Fail(payment, CreateErrorResult(result.RequestMessage.Content)), invoice, true)); } // Process the response from SagePay - this contains the redirect URL for the customer to complete their payment. var redirectUrl = result.RequestMessage.RequestUri.ToString(); payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl); } catch (Exception ex) { LogHelper.Error <SagePayFormPaymentProcessor>("SagePay form post failed. Crypt value: " + request.Crypt, ex); } // Store our site return URL and cancel URL in extendedData so it can be used in the callback var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); } catch (Exception ex) { return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, true)); } }
//TODO: refactor away to a Service that wraps the SagePay kit horribleness private void SetSagePayApiData(IFormPayment request, IInvoice invoice, IPayment payment) { // Get Merchello data var billingAddress = invoice.GetBillingAddress(); var shippingAddress = billingAddress; // Shipment - only use a shipping address if there is shipment info in the invoice var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault(); if (shipmentLineItem != null) { var shipment = shipmentLineItem.ExtendedData.GetShipment <InvoiceLineItem>(); shippingAddress = shipment.GetDestinationAddress(); } // SagePay details request.VpsProtocol = Settings.ProtocolVersion; request.TransactionType = Settings.TransactionType; request.Vendor = Settings.VendorName; request.VendorTxCode = SagePayFormIntegration.GetNewVendorTxCode(); request.Amount = payment.Amount; request.Currency = invoice.CurrencyCode(); request.Description = "Goods from " + Settings.VendorName; // TODO: Is there a basket summary I can access? Or convert the Basket to a sagepay format // Set ReturnUrl and CancelUrl of SagePay request to SagePayApiController. Func <string, string> adjustUrl = (url) => { if (!url.StartsWith("http")) { url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url; } url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); return(url); }; request.SuccessUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/SuccessPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"); request.FailureUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/AbortPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"); // Billing details request.BillingSurname = billingAddress.TrySplitLastName(); request.BillingFirstnames = billingAddress.TrySplitFirstName(); request.BillingAddress1 = billingAddress.Address1; request.BillingAddress2 = billingAddress.Address2; request.BillingPostCode = billingAddress.PostalCode; request.BillingCity = billingAddress.Locality; request.BillingCountry = invoice.BillToCountryCode; request.CustomerEmail = billingAddress.Email; // Shipping details request.DeliverySurname = shippingAddress.TrySplitLastName(); request.DeliveryFirstnames = shippingAddress.TrySplitFirstName(); request.DeliveryAddress1 = shippingAddress.Address1; request.DeliveryCity = shippingAddress.Locality; request.DeliveryCountry = shippingAddress.CountryCode; request.DeliveryPostCode = shippingAddress.PostalCode; //Optional //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname; //request.VendorEmail = Settings.VendorEmail; //request.SendEmail = Settings.SendEmail; //request.EmailMessage = Settings.EmailMessage; //request.BillingAddress2 = billingAddress.Address2; //request.BillingPostCode = billingAddress.PostalCode; //request.BillingState = billingAddress.Region; //request.BillingPhone = billingAddress.Phone; //request.DeliveryAddress2 = shippingAddress.Address2; //request.DeliveryPostCode = shippingAddress.PostalCode; //request.DeliveryState = shippingAddress.Region; //request.DeliveryPhone = shippingAddress.Phone; }
/// <summary> /// Processes the Authorize and AuthorizeAndCapture transactions /// </summary> /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param> /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param> /// <param name="args"></param> /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns> public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { try { var sagePayDirectIntegration = new SagePayAPIIntegration(Settings); var request = sagePayDirectIntegration.DirectPaymentRequest(); var creditCard = args.AsCreditCard(); SetSagePayApiData(request, invoice, payment, creditCard); // Incredibly frustratingly, the sagepay integration kit provided by sagepay does not support paypal integration with sagepay direct so if this is a paypal transaction, we have to build the post manually. if (request.CardType == CardType.PAYPAL) { using (var client = new HttpClient()) { var values = new Dictionary <string, string> { }; // Build the post and fix the values that the integration kit breaks... foreach (var property in request.GetType().GetAllProperties()) { if (property.CanRead && property.GetValue(request) != null) { if (property.Name == "VpsProtocol") { values.Add(property.Name, "3.00"); } else if (property.Name == "TransactionType") { values.Add("TxType", "PAYMENT"); } else { values.Add(property.Name, property.GetValue(request).ToString()); } } } var content = new FormUrlEncodedContent(values); var response = client.PostAsync(string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment), content).Result; var responseString = response.Content.ReadAsStringAsync().Result.Replace("\r\n", "&"); NameValueCollection sagePayResponseValues = HttpUtility.ParseQueryString(responseString); if (sagePayResponseValues["Status"] == "PPREDIRECT") { var redirectUrl = sagePayResponseValues["PayPalRedirectURL"] + "&token=" + sagePayResponseValues["token"]; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl); var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayTransactionCode, sagePayResponseValues["VPSTxId"]); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); } else { return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception(sagePayResponseValues["StatusDetail"])), invoice, true)); } } } IDirectPaymentResult result = sagePayDirectIntegration.ProcessDirectPaymentRequest(request, string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment)); if (result.Status == ResponseStatus.OK) { payment.Collected = true; payment.Authorized = true; GatewayProviderService service = new GatewayProviderService(); service.ApplyPaymentToInvoice(payment.Key, invoice.Key, Core.AppliedPaymentType.Debit, "SagePay: capture authorized", invoice.Total); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); } else if (result.Status == ResponseStatus.THREEDAUTH) { // For 3D Secure we have to show a client side form which posts to the ACS url. Func <string, string> adjustUrl = (url) => { if (!url.StartsWith("http")) { url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url; } url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); return(url); }; var redirectUrl = adjustUrl("/App_Plugins/Merchello.SagePay/3dsecureRedirect.aspx?"); redirectUrl += "acsurl=" + Base64Encode(result.AcsUrl); redirectUrl += "&PaReq=" + Base64Encode(result.PaReq); redirectUrl += "&MD=" + Base64Encode(result.Md); redirectUrl += "&TermUrl=" + Base64Encode(adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/ThreeDSecureCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}")); payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ThreeDSecureUrl, redirectUrl); var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, true)); } //else if (result.Status == ResponseStatus.PPREDIRECT) //{ // var redirectUrl = result.PayPalRedirectUrl + "&token=" + result.Token; // payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl); // var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; // payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); // var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; // payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); // return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); //} else { return(new PaymentResult(Attempt <IPayment> .Fail(payment, new Exception(result.StatusDetail)), invoice, true)); } } catch (Exception ex) { return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, true)); } }
//TODO: refactor away to a Service that wraps the SagePay kit horribleness private void SetSagePayApiData(IDirectPayment request, IInvoice invoice, IPayment payment, CreditCard creditCard) { // Get Merchello data //TODO - what if there is no shipping info? e.g. Classes only - Get from billing? var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault(); var shipment = shipmentLineItem.ExtendedData.GetShipment <InvoiceLineItem>(); var shippingAddress = shipment.GetDestinationAddress(); var billingAddress = invoice.GetBillingAddress(); // Merchello info for callback //request.InvoiceKey = invoice.Key; //request.PayerId = invoice.Pa //request.PaymentKey = payment.Key // SagePay details request.VpsProtocol = Settings.ProtocolVersion; request.TransactionType = Settings.TransactionType; request.Vendor = Settings.VendorName; request.VendorTxCode = SagePayAPIIntegration.GetNewVendorTxCode(); request.Amount = payment.Amount; request.Currency = invoice.CurrencyCode(); request.Description = "Goods from " + Settings.VendorName; // TODO: Is there a basket summary I can access? Or convert the Basket to a sagepay format // Billing details request.BillingSurname = billingAddress.TrySplitLastName(); request.BillingFirstnames = billingAddress.TrySplitFirstName(); request.BillingAddress1 = billingAddress.Address1; request.BillingAddress2 = billingAddress.Address2; request.BillingPostCode = billingAddress.PostalCode; request.BillingCity = billingAddress.Locality; request.BillingCountry = invoice.BillToCountryCode; // Shipping details request.DeliverySurname = shippingAddress.TrySplitLastName(); request.DeliveryFirstnames = shippingAddress.TrySplitFirstName(); request.DeliveryAddress1 = shippingAddress.Address1; request.DeliveryCity = shippingAddress.Locality; request.DeliveryCountry = shippingAddress.CountryCode; request.DeliveryPostCode = shippingAddress.PostalCode; request.CardType = (CardType)Enum.Parse(typeof(CardType), creditCard.CreditCardType); request.CardHolder = creditCard.CardholderName; request.CardNumber = creditCard.CardNumber; request.ExpiryDate = creditCard.ExpireMonth + creditCard.ExpireYear; request.Cv2 = creditCard.CardCode; request.Apply3dSecure = 0; if (request.CardType == CardType.PAYPAL) { Func <string, string> adjustUrl = (url) => { if (!url.StartsWith("http")) { url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url; } url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); return(url); }; request.PayPalCallbackUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/PaypalCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"); } //Optional //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname; //request.CustomerEmail = customer.Email; //request.VendorEmail = Settings.VendorEmail; //request.SendEmail = Settings.SendEmail; //request.EmailMessage = Settings.EmailMessage; //request.BillingAddress2 = billingAddress.Address2; //request.BillingPostCode = billingAddress.PostalCode; //request.BillingState = billingAddress.Region; //request.BillingPhone = billingAddress.Phone; //request.DeliveryAddress2 = shippingAddress.Address2; //request.DeliveryPostCode = shippingAddress.PostalCode; //request.DeliveryState = shippingAddress.Region; //request.DeliveryPhone = shippingAddress.Phone; }
/// <summary> /// Create a dictionary with credentials for SagePay service. /// </summary> /// <param name="settings"></param> /// <returns></returns> //private static Dictionary<string, string> CreateSagePayApiConfig(SagePayProcessorSettings settings) //{ // return new Dictionary<string, string> // { // {"mode", GetModeString(settings.LiveMode)}, // {"account.vendorName", settings.VendorName}, // {"account.encyptionPassword", settings.EncyptionPassword}, // {"account.apiVersion", settings.ApiVersion} // }; //} /// <summary> /// Processes the Authorize and AuthorizeAndCapture transactions /// </summary> /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param> /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param> /// <param name="args"></param> /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns> public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { try { // Gather SagePay settings info and formulate it into a Dictionary for posting to the gateway var sagePayFormIntegration = new SagePayFormIntegration(Settings); var request = sagePayFormIntegration.FormPaymentRequest(); SetSagePayApiData(request, invoice, payment); // Do basic validation as per the SagePay kit var errors = sagePayFormIntegration.Validation(request); if (errors.Count > 0) { return new PaymentResult(Attempt<IPayment>.Fail(payment, base.CreateErrorResult(errors)), invoice, true); } // Use the SagePay methods to encrypt the form so it can be posted sagePayFormIntegration.ProcessRequest(request); // Prepare a HTTP post var content = new FormUrlEncodedContent( new Dictionary<string, string> { {"VPSProtocol", Settings.ApiVersion}, {"TxType", Constants.TransactionType}, {"Vendor", Settings.VendorName}, //{"Profile", "LOW"}, // Provider setting? {"Crypt", request.Crypt} }); try { // Post the form to SagePay VSP var formPaymentUrl = string.Format("https://{0}.sagepay.com/gateway/service/vspform-register.vsp", Settings.Environment); // TEST form post //var formPaymentUrl = string.Format("https://{0}.sagepay.com/showpost/showpost.asp", GetModeString(Settings.LiveMode)); var result = new HttpClient().PostAsync(formPaymentUrl, content).Result; // Store transaction details in ExtendedData payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.VendorTransactionCode, request.VendorTxCode); // Flag an error in the backoffice if the result is not successful if (!result.IsSuccessStatusCode) { return new PaymentResult(Attempt<IPayment>.Fail(payment, CreateErrorResult(result.RequestMessage.Content)), invoice, true); } // Process the response from SagePay - this contains the redirect URL for the customer to complete their payment. var redirectUrl = result.RequestMessage.RequestUri.ToString(); payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl); } catch (Exception ex) { LogHelper.Error<SagePayFormPaymentProcessor>("SagePay form post failed. Crypt value: " + request.Crypt, ex); } // Store our site return URL and cancel URL in extendedData so it can be used in the callback var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); } catch (Exception ex) { return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, true); } }
//TODO: refactor away to a Service that wraps the SagePay kit horribleness private void SetSagePayApiData(IFormPayment request, IInvoice invoice, IPayment payment) { // Get Merchello data var billingAddress = invoice.GetBillingAddress(); var shippingAddress = billingAddress; // Shipment - only use a shipping address if there is shipment info in the invoice var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault(); if (shipmentLineItem != null) { var shipment = shipmentLineItem.ExtendedData.GetShipment<InvoiceLineItem>(); shippingAddress = shipment.GetDestinationAddress(); } // SagePay details request.VpsProtocol = Settings.ProtocolVersion; request.TransactionType = Settings.TransactionType; request.Vendor = Settings.VendorName; request.VendorTxCode = SagePayFormIntegration.GetNewVendorTxCode(); request.Amount = payment.Amount; request.Currency = invoice.CurrencyCode(); request.Description = "Goods from " + Settings.VendorName; // TODO: Is there a basket summary I can access? Or convert the Basket to a sagepay format // Set ReturnUrl and CancelUrl of SagePay request to SagePayApiController. Func<string, string> adjustUrl = (url) => { if (!url.StartsWith("http")) url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url; url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); return url; }; request.SuccessUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/SuccessPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"); request.FailureUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/AbortPayment?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"); // Billing details request.BillingSurname = billingAddress.TrySplitLastName(); request.BillingFirstnames = billingAddress.TrySplitFirstName(); request.BillingAddress1 = billingAddress.Address1; request.BillingAddress2 = billingAddress.Address2; request.BillingPostCode = billingAddress.PostalCode; request.BillingCity = billingAddress.Locality; request.BillingCountry = invoice.BillToCountryCode; request.CustomerEmail = billingAddress.Email; // Shipping details request.DeliverySurname = shippingAddress.TrySplitLastName(); request.DeliveryFirstnames = shippingAddress.TrySplitFirstName(); request.DeliveryAddress1 = shippingAddress.Address1; request.DeliveryCity = shippingAddress.Locality; request.DeliveryCountry = shippingAddress.CountryCode; request.DeliveryPostCode = shippingAddress.PostalCode; //Optional //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname; //request.VendorEmail = Settings.VendorEmail; //request.SendEmail = Settings.SendEmail; //request.EmailMessage = Settings.EmailMessage; //request.BillingAddress2 = billingAddress.Address2; //request.BillingPostCode = billingAddress.PostalCode; //request.BillingState = billingAddress.Region; //request.BillingPhone = billingAddress.Phone; //request.DeliveryAddress2 = shippingAddress.Address2; //request.DeliveryPostCode = shippingAddress.PostalCode; //request.DeliveryState = shippingAddress.Region; //request.DeliveryPhone = shippingAddress.Phone; }
/// <summary> /// Processes the Authorize and AuthorizeAndCapture transactions /// </summary> /// <param name="invoice">The <see cref="IInvoice"/> to be paid</param> /// <param name="payment">The <see cref="Core.Models.IPayment"/> record</param> /// <param name="args"></param> /// <returns>The <see cref="Core.Gateways.Payment.IPaymentResult"/></returns> public IPaymentResult InitializePayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { try { var sagePayDirectIntegration = new SagePayAPIIntegration(Settings); var request = sagePayDirectIntegration.DirectPaymentRequest(); var creditCard = args.AsCreditCard(); SetSagePayApiData(request, invoice, payment, creditCard); // Incredibly frustratingly, the sagepay integration kit provided by sagepay does not support paypal integration with sagepay direct so if this is a paypal transaction, we have to build the post manually. if (request.CardType == CardType.PAYPAL) { using (var client = new HttpClient()) { var values = new Dictionary<string, string> { }; // Build the post and fix the values that the integration kit breaks... foreach (var property in request.GetType().GetAllProperties()) { if (property.CanRead && property.GetValue(request) != null) { if (property.Name == "VpsProtocol") { values.Add(property.Name, "3.00"); } else if (property.Name == "TransactionType") { values.Add("TxType", "PAYMENT"); } else { values.Add(property.Name, property.GetValue(request).ToString()); } } } var content = new FormUrlEncodedContent(values); var response = client.PostAsync(string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment), content).Result; var responseString = response.Content.ReadAsStringAsync().Result.Replace("\r\n", "&"); NameValueCollection sagePayResponseValues = HttpUtility.ParseQueryString(responseString); if (sagePayResponseValues["Status"] == "PPREDIRECT") { var redirectUrl = sagePayResponseValues["PayPalRedirectURL"] + "&token=" + sagePayResponseValues["token"]; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl); var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayTransactionCode, sagePayResponseValues["VPSTxId"]); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); } else { return new PaymentResult(Attempt<IPayment>.Fail(payment, new Exception(sagePayResponseValues["StatusDetail"])), invoice, true); } } } IDirectPaymentResult result = sagePayDirectIntegration.ProcessDirectPaymentRequest(request, string.Format("https://{0}.sagepay.com/gateway/service/vspdirect-register.vsp", Settings.Environment)); if (result.Status == ResponseStatus.OK) { payment.Collected = true; payment.Authorized = true; GatewayProviderService service = new GatewayProviderService(); service.ApplyPaymentToInvoice(payment.Key, invoice.Key, Core.AppliedPaymentType.Debit, "SagePay: capture authorized", invoice.Total); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); } else if (result.Status == ResponseStatus.THREEDAUTH) { // For 3D Secure we have to show a client side form which posts to the ACS url. Func<string, string> adjustUrl = (url) => { if (!url.StartsWith("http")) url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url; url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); return url; }; var redirectUrl = adjustUrl("/App_Plugins/Merchello.SagePay/3dsecureRedirect.aspx?"); redirectUrl += "acsurl=" + Base64Encode(result.AcsUrl); redirectUrl += "&PaReq=" + Base64Encode(result.PaReq); redirectUrl += "&MD=" + Base64Encode(result.Md); redirectUrl += "&TermUrl=" + Base64Encode(adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/ThreeDSecureCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}")); payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ThreeDSecureUrl, redirectUrl); var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); } //else if (result.Status == ResponseStatus.PPREDIRECT) //{ // var redirectUrl = result.PayPalRedirectUrl + "&token=" + result.Token; // payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayPaymentUrl, redirectUrl); // var returnUrl = GetWebsiteUrl() + Settings.ReturnUrl; // payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.ReturnUrl, returnUrl); // var cancelUrl = GetWebsiteUrl() + Settings.CancelUrl; // payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.CancelUrl, cancelUrl); // return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, true); //} else { return new PaymentResult(Attempt<IPayment>.Fail(payment, new Exception(result.StatusDetail)), invoice, true); } } catch (Exception ex) { return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, true); } }
//TODO: refactor away to a Service that wraps the SagePay kit horribleness private void SetSagePayApiData(IDirectPayment request, IInvoice invoice, IPayment payment, CreditCard creditCard) { // Get Merchello data //TODO - what if there is no shipping info? e.g. Classes only - Get from billing? var shipmentLineItem = invoice.ShippingLineItems().FirstOrDefault(); var shipment = shipmentLineItem.ExtendedData.GetShipment<InvoiceLineItem>(); var shippingAddress = shipment.GetDestinationAddress(); var billingAddress = invoice.GetBillingAddress(); // Merchello info for callback //request.InvoiceKey = invoice.Key; //request.PayerId = invoice.Pa //request.PaymentKey = payment.Key // SagePay details request.VpsProtocol = Settings.ProtocolVersion; request.TransactionType = Settings.TransactionType; request.Vendor = Settings.VendorName; request.VendorTxCode = SagePayAPIIntegration.GetNewVendorTxCode(); request.Amount = payment.Amount; request.Currency = invoice.CurrencyCode(); request.Description = "Goods from " + Settings.VendorName; // TODO: Is there a basket summary I can access? Or convert the Basket to a sagepay format // Billing details request.BillingSurname = billingAddress.TrySplitLastName(); request.BillingFirstnames = billingAddress.TrySplitFirstName(); request.BillingAddress1 = billingAddress.Address1; request.BillingAddress2 = billingAddress.Address2; request.BillingPostCode = billingAddress.PostalCode; request.BillingCity = billingAddress.Locality; request.BillingCountry = invoice.BillToCountryCode; // Shipping details request.DeliverySurname = shippingAddress.TrySplitLastName(); request.DeliveryFirstnames = shippingAddress.TrySplitFirstName(); request.DeliveryAddress1 = shippingAddress.Address1; request.DeliveryCity = shippingAddress.Locality; request.DeliveryCountry = shippingAddress.CountryCode; request.DeliveryPostCode = shippingAddress.PostalCode; request.CardType = (CardType)Enum.Parse(typeof(CardType), creditCard.CreditCardType); request.CardHolder = creditCard.CardholderName; request.CardNumber = creditCard.CardNumber; request.ExpiryDate = creditCard.ExpireMonth + creditCard.ExpireYear; request.Cv2 = creditCard.CardCode; request.Apply3dSecure = 0; if (request.CardType == CardType.PAYPAL) { Func<string, string> adjustUrl = (url) => { if (!url.StartsWith("http")) url = GetWebsiteUrl() + (url[0] == '/' ? "" : "/") + url; url = url.Replace("{invoiceKey}", invoice.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); url = url.Replace("{paymentKey}", payment.Key.ToString(), StringComparison.InvariantCultureIgnoreCase); return url; }; request.PayPalCallbackUrl = adjustUrl("/umbraco/MerchelloSagePay/SagePayApi/PaypalCallback?InvoiceKey={invoiceKey}&PaymentKey={paymentKey}"); } //Optional //request.CustomerName = cart.Billing.FirstNames + " " + cart.Billing.Surname; //request.CustomerEmail = customer.Email; //request.VendorEmail = Settings.VendorEmail; //request.SendEmail = Settings.SendEmail; //request.EmailMessage = Settings.EmailMessage; //request.BillingAddress2 = billingAddress.Address2; //request.BillingPostCode = billingAddress.PostalCode; //request.BillingState = billingAddress.Region; //request.BillingPhone = billingAddress.Phone; //request.DeliveryAddress2 = shippingAddress.Address2; //request.DeliveryPostCode = shippingAddress.PostalCode; //request.DeliveryState = shippingAddress.Region; //request.DeliveryPhone = shippingAddress.Phone; }