/// <summary> /// Saves/updates the payment method for the member, at the remote gateway, /// updates in place, and returns, the ID/token /// </summary> /// <returns>The saved payment method ID/token</returns> /// <param name="paymentData"></param> private static string CollectPaymentMethod(LcPayment.InputPaymentMethod paymentData, int memberUserID) { // On emulation, discard other steps, just generate // a fake ID if (LcPayment.TESTING_EMULATEBRAINTREE) { paymentData.paymentMethodID = LcPayment.CreateFakePaymentMethodId(); return(paymentData.paymentMethodID); } // Standard way var gateway = LcPayment.NewBraintreeGateway(); // The input paymentID must be one generated by Braintree, reset any (malicious?) attempt // to provide a special temp ID generated by this method if (paymentData.IsTemporaryID()) { paymentData.paymentMethodID = null; } // Find or create Customer on Braintree (for membership subscriptions, the member // is a customer of Loconomics). var client = LcPayment.GetOrCreateBraintreeCustomer(LcPayment.Membership.GetFeePaymentUserId(memberUserID)); // Quick way for saved payment method that does not needs to be updated if (paymentData.IsSavedID()) { // Just double check payment exists to avoid mistake/malicious attempts: if (!paymentData.ExistsOnVault()) { // Since we have not input data to save, we can only throw an error // invalidSavedPaymentMethod throw new ConstraintException("[[[Chosen payment method has expired]]]"); } } else { // Creates or updates a payment method with the given data // Must we set an ID as temporary to prevent it appears as a saved payment method? //paymentData.paymentMethodID = LcPayment.TempSavedCardPrefix + ASP.LcHelpers.Channel + "_paymentPlan"; // Save on Braintree secure Vault // It updates the paymentMethodID if a new one was generated var saveCardError = paymentData.SaveInVault(client.Id); if (!String.IsNullOrEmpty(saveCardError)) { // paymentDataError throw new ConstraintException(saveCardError); } } return(paymentData.paymentMethodID); }
public static UserPaymentPlan CreateSubscription( int userID, SubscriptionPlan plan, LcPayment.InputPaymentMethod paymentMethod) { // Prepare payment method (in the remote gateway), get its ID var paymentMethodToken = CollectPaymentMethod(paymentMethod, userID); // Prepare initial object var userPlan = new UserPaymentPlan() { userID = userID, paymentPlan = plan, subscriptionEndDate = null }; // Create subscription at gateway and set details // Wrapped in a try-catch to implement a transaction-like operation: // if something fail after succesfully create the Braintree subscription, like not being // able to save details on database, we need to 'rollback' the subscription, asking for removal // to Braintree string generatedSubscriptionId = null; var paymentPlan = new LcPayment.Membership(); try { if (LcPayment.TESTING_EMULATEBRAINTREE) { userPlan.subscriptionID = LcPayment.CreateFakeSubscriptionId(); userPlan.paymentPlanLastChangedDate = DateTimeOffset.Now; userPlan.nextPaymentDueDate = DateTimeOffset.Now.Add(new TimeSpan(365, 0, 0, 0)); userPlan.nextPaymentAmount = 99; userPlan.firstBillingDate = DateTimeOffset.Now; userPlan.planStatus = "ACTIVE"; userPlan.daysPastDue = 0; } else { // Start creating the subscription at the payment gateway var trialEndDate = GetUserTrialEndDate(userID); // Create the subscription at the payment gateway // It returns the subscription object with a correct ID on success, otherwise an exception is thrown var subscription = paymentPlan.CreateSubscription(plan, paymentMethodToken, trialEndDate); generatedSubscriptionId = subscription.Id; userPlan.subscriptionID = subscription.Id; userPlan.paymentPlanLastChangedDate = subscription.UpdatedAt.Value; userPlan.nextPaymentDueDate = subscription.NextBillingDate; userPlan.nextPaymentAmount = subscription.NextBillAmount; userPlan.firstBillingDate = subscription.FirstBillingDate.Value; userPlan.planStatus = subscription.Status.ToString(); userPlan.daysPastDue = subscription.DaysPastDue ?? 0; } // Fill payment method info var info = LcPayment.PaymentMethodInfo.Get(paymentMethodToken); userPlan.paymentExpiryDate = info.ExpirationDate; userPlan.paymentMethodToken = paymentMethodToken; userPlan.paymentMethod = info.Description; // Persist subscription on database Set(userPlan); } catch (Exception ex) { // Rollback if (generatedSubscriptionId != null) { // Rollback subscription at Payment Gateway paymentPlan.CancelSubscription(generatedSubscriptionId); } // The exception needs to be communicated anyway, so re-throw throw new Exception("[[[Failed subscription]]]", ex); } return(userPlan); }