public IHttpActionResult ProcessPayment(FormCollection parameters) { //first get the payment processor var paymentMethodName = parameters.Get(PaymentParameterNames.PaymentMethodTypeName); if (string.IsNullOrEmpty(paymentMethodName)) { VerboseReporter.ReportError("Invalid payment method", "process_payment"); return RespondFailure(); } //the transaction amount decimal amount; var amountString = parameters.Get(PaymentParameterNames.Amount) ?? "0"; decimal.TryParse(amountString, out amount); PaymentMethodType methodType; if (System.Enum.TryParse(paymentMethodName, out methodType)) { methodType = PaymentMethodType.CreditCard; } //get the payment processor now var paymentProcessor = _paymentProcessingService.GetPaymentProcessorPlugin(amount, methodType); if (paymentProcessor == null) { VerboseReporter.ReportError("Invalid payment method", "process_payment"); return RespondFailure(); } //convert form collection to dictionary to check if parameters are valid var formCollectionDictionary = parameters.ToDictionary(pair => pair.Key, pair => (object)pair.Value); var isValid = paymentProcessor.AreParametersValid(formCollectionDictionary); UserPaymentMethod paymentMethod = null; if (!isValid) { //the parameters are not valid. but that may also mean that the user is selecting an already saved payment method //and so he wouldn't have sent that data again var savedPaymentMethodIdString = parameters.Get(PaymentParameterNames.UserSavedPaymentMethodId); int savedPaymentMethodId; if (int.TryParse(savedPaymentMethodIdString, out savedPaymentMethodId)) { var userPaymentMethods = _paymentMethodService.Get(x => x.UserId == ApplicationContext.Current.CurrentUser.Id && x.Id == savedPaymentMethodId, null); if (userPaymentMethods.Any()) { paymentMethod = userPaymentMethods.First(); isValid = true; } } //still invalid? something is not right then. if (!isValid) { VerboseReporter.ReportError("Invalid parameters to process payment", "process_payment"); return RespondFailure(); } } //we save the payment method in our database if it's CreditCard if (paymentProcessor.Supports(PaymentMethodType.CreditCard)) { if (paymentMethod == null) { #region saving payment method to database var creditCardNumber = parameters.Get(PaymentParameterNames.CardNumber); //let's validate the card for level 1 check (luhn's test) first before storing var isCardValid = PaymentCardHelper.IsCardNumberValid(creditCardNumber); //card number if (!isCardValid) { VerboseReporter.ReportError("Invalid card number", "process_payment"); return RespondFailure(); } //expiration date var expireMonth = parameters.Get(PaymentParameterNames.ExpireMonth); var expireYear = parameters.Get(PaymentParameterNames.ExpireYear); if (!expireYear.IsInteger() || !expireMonth.IsInteger()) { VerboseReporter.ReportError("Invalid expiration month or year", "process_payment"); return RespondFailure(); } //card issuer var cardIssuer = PaymentCardHelper.GetCardTypeFromNumber(creditCardNumber); if (!cardIssuer.HasValue) { VerboseReporter.ReportError("Unsupported card provider", "process_payment"); return RespondFailure(); } var nameOnCard = parameters.Get(PaymentParameterNames.NameOnCard); //encrypt credit card info to store in db var key = ConfigurationManager.AppSettings.Get("EncryptionKey"); var salt = ConfigurationManager.AppSettings.Get("Salt"); var cardNumber = _cryptographyService.Encrypt(creditCardNumber, key, salt); //encrypt the card info //fine if the card is valid, but is the card number already in our record, then not possible to save the same again if (_paymentMethodService.DoesCardNumberExist(cardNumber)) { VerboseReporter.ReportError("The card number is already saved in records", "process_payment"); return RespondFailure(); } paymentMethod = new UserPaymentMethod() { UserId = ApplicationContext.Current.CurrentUser.Id, IsVerified = false, PaymentMethodType = PaymentMethodType.CreditCard, CardIssuerType = cardIssuer.ToString().ToLowerInvariant(), CardNumber = creditCardNumber, CardNumberMasked = PaymentCardHelper.MaskCardNumber(creditCardNumber), NameOnCard = nameOnCard, }; //save this payment method _paymentMethodService.Insert(paymentMethod); #endregion } } //we need to see if we should only authorize or capture as well //the idea is if it's a sponsorship context, it's better to authorize the payment transaction and capture later when //the sponsorship is accepted //we thought of initially only authorizing sponsorship transactions and capture when it's accepted. //but that flow doesn't seem to work quite well, thoughts? var authorizeOnly = false; // (parameters.Get(PaymentParameterNames.PaymentContext) ?? string.Empty) == "sponsor"; //so we are ready for payment processing, let's create a paymenttrasaction for storing in our db var paymentTransaction = new PaymentTransaction() { IsLocalTransaction = true, PaymentStatus = PaymentStatus.Pending, TransactionAmount = amount, TransactionGuid = Guid.NewGuid(), CreatedOn = DateTime.UtcNow, UserIpAddress = WebHelper.GetClientIpAddress() }; _paymentTransactionService.Insert(paymentTransaction); //now proceed further with the payment //create the transaction request var transactionRequest = new TransactionRequest() { Amount = amount, CurrencyIsoCode = "USD",//TODO: SET CURRENCY AS SELECTED BY USER PaymentProcessorSystemName = paymentProcessor.PluginInfo.SystemName, UserId = ApplicationContext.Current.CurrentUser.Id, Parameters = formCollectionDictionary, TransactionUniqueId = paymentTransaction.TransactionGuid.ToString() }; var response = paymentProcessor.Process(transactionRequest, authorizeOnly); //update values of transaction parameters for future reference paymentTransaction.TransactionCodes = response.ResponseParameters; //update payment transaction _paymentTransactionService.Update(paymentTransaction); if (response.Success) { //let's verify the payment method first if it's not if (paymentMethod != null && !paymentMethod.IsVerified) { paymentMethod.IsVerified = true; _paymentMethodService.Update(paymentMethod); } //now since the response was success, we can actually assign some credits to the user var creditCount = amount * (1 / _paymentSettings.CreditExchangeRate); var credit = new Credit() { PaymentTransactionId = paymentTransaction.Id, CreatedOnUtc = DateTime.UtcNow, CreditCount = creditCount, CreditExchangeRate = _paymentSettings.CreditExchangeRate, //if it's authorize only transaction, we assign the credits, but they won't be usable before they are approved by capture CreditTransactionType = CreditTransactionType.Issued, CreditType = CreditType.Transactional, IsExpired = false }; //save credit _creditService.Insert(credit); //get total available credits of user var usableCreditCount = _creditService.GetUsableCreditsCount(ApplicationContext.Current.CurrentUser.Id); return RespondSuccess(new { UsableCreditCount = usableCreditCount }); } VerboseReporter.ReportError("An error occured while processing payment", "process_payment"); return RespondFailure(); }
public ITransactionProcessResult ProcessPayment(User user, UserPaymentMethod paymentMethod, ITransactionProcessRequest processRequest, bool authorizeOnly = false) { if(processRequest == null) throw new mobSocialException("Can't process null request"); //check if the plugin is installed and activated //depending on the payment amount, we may wish to switch between macro and micro payments //check if plugin is installed or not //TODO: Change the other payment method to appropriate one for macro payments var paymentPluginSystemName = processRequest.Amount < MicroMacroPaymentSwitchingAmount ? "Payments.PayPalDirect" : "Payments.PayPalDirect"; //if we get an instance, then the plugin is installed var plugin = GetPluginInstance(paymentPluginSystemName); //plugin should be available and if it's an authorization transaction, the plugin should actually support authorize and capture if (plugin == null || (!plugin.CaptureSupported && authorizeOnly)) return null; //process the payment now var result = plugin.Process(processRequest); return result; }