public HttpResponseMessage SuccessPayment(Guid invoiceKey, Guid paymentKey, string crypt) { // Decrypt sagepay querystring data message if (string.IsNullOrEmpty(crypt)) { var ex = new NullReferenceException(string.Format("Invalid argument exception. Arguments: crypt={0}", crypt)); LogHelper.Error<SagePayApiController>("Payment not authorized.", ex); throw ex; } var sagePayFormIntegration = new SagePayFormIntegration(_formProcessor.Settings); var paymentResult = sagePayFormIntegration.ProcessResult(crypt); if (paymentResult.Status != ResponseStatus.OK && paymentResult.Status != ResponseStatus.AUTHENTICATED && paymentResult.Status != ResponseStatus.REGISTERED) { //var ex = new Exception(string.Format("Invalid payment status. Detail: {0}", paymentResult.StatusDetail)); //LogHelper.Error<SagePayApiController>("Sagepay error processing payment.", ex); return ShowError(paymentResult.StatusDetail); } // Query merchello for associated invoice and payment objects var invoice = _merchelloContext.Services.InvoiceService.GetByKey(invoiceKey); var payment = _merchelloContext.Services.PaymentService.GetByKey(paymentKey); if (invoice == null || payment == null || invoice.CustomerKey == null) { var ex = new NullReferenceException( string.Format( "Invalid argument exception. Arguments: invoiceKey={0}, paymentKey={1}", invoiceKey, paymentKey)); LogHelper.Error<SagePayApiController>("Payment not authorized.", ex); throw ex; } // Get a ref to the customer so the invoice Key can be stored in their extended data. // This can be retrieved on the receipt page //var customer = _merchelloContext.Services.CustomerService.GetByKey(invoice.CustomerKey.Value); //customer.ExtendedData.SetValue(Constants.ExtendedDataKeys.InvoiceKey, invoice.Key.ToString()); // Store some SagePay data in payment payment.ReferenceNumber = paymentResult.VpsTxId; payment.ExtendedData.SetValue(Constants.ExtendedDataKeys.SagePayTransactionCode, paymentResult.VpsTxId); // Authorize and save payment var authorizeResult = _formProcessor.AuthorizePayment(invoice, payment); _merchelloContext.Services.GatewayProviderService.Save(payment); if (!authorizeResult.Payment.Success) { LogHelper.Error<SagePayApiController>("Payment is not authorized.", authorizeResult.Payment.Exception); _merchelloContext.Services.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Denied, "SagePay: request capture authorization error: " + authorizeResult.Payment.Exception.Message, 0); return ShowError(authorizeResult.Payment.Exception.Message); } _merchelloContext.Services.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "SagePay: capture authorized", 0); // Capture payment var providerKeyGuid = new Guid(Constants.GatewayProviderSettingsKey); var paymentGatewayMethod = _merchelloContext.Gateways.Payment.GetPaymentGatewayMethods().First(item => item.PaymentMethod.ProviderKey == providerKeyGuid); var captureResult = paymentGatewayMethod.CapturePayment(invoice, payment, payment.Amount, null); if (!captureResult.Payment.Success) { LogHelper.Error<SagePayApiController>("Payment not captured.", captureResult.Payment.Exception); return ShowError(captureResult.Payment.Exception.Message); } // Redirect to ReturnUrl (with token replacement for an alternative means of order retrieval) var returnUrl = payment.ExtendedData.GetValue(Constants.ExtendedDataKeys.ReturnUrl); var response = Request.CreateResponse(HttpStatusCode.Moved); response.Headers.Location = new Uri(returnUrl.Replace("%INVOICE%", invoice.Key.ToString().EncryptWithMachineKey())); return response; }
/// <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); } }