Commits

ems  committed 3d7b6e9 Merge

Merge

  • Participants
  • Parent commits df60ad4, a56b77c

Comments (0)

Files changed (76)

File Activities/OrderActivity.cs

     }
 
     [OrchardFeature("Nwazet.Orders")]
+    public class OrderError : OrderActivity {
+        public override string Name {
+            get { return "OrderError"; }
+        }
+
+        public override LocalizedString Description {
+            get { return T("An order resulted in an error."); }
+        }
+    }
+
+    [OrchardFeature("Nwazet.Orders")]
     public class OrderStatusChanged : OrderActivity {
         public override string Name {
             get { return "OrderStatusChanged"; }

File Content/Web.config

 <?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <system.webServer>
+    <staticContent>
+      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
+    </staticContent>
 
-<configuration>
-	<appSettings>
-		<add key="webpages:Enabled" value="false" />
-	</appSettings>
-	<system.web>
-		<httpHandlers>
-			<!-- iis6 - for any request in this location, return via managed static file handler -->
-			<add path="*" verb="*" type="System.Web.StaticFileHandler" />
-		</httpHandlers>
-	</system.web>
-	<system.webServer>
-		<handlers accessPolicy="Script,Read">
-			<!--
+    <handlers accessPolicy="Script,Read">
+      <!--
       iis7 - for any request to a file exists on disk, return it via native http module.
       accessPolicy 'Script' is to allow for a managed 404 page.
       -->
-			<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
-		</handlers>
-	</system.webServer>
-</configuration>
+      <add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
+    </handlers>
+  </system.webServer>
+</configuration>

File Controllers/OrderAdminController.cs

             var query = _contentManager.Query<OrderPart, OrderPartRecord>(VersionOptions.Latest);
             var states = OrderPart.States.ToList();
 
-            if (model.Options != null) {
-                if (!string.IsNullOrWhiteSpace(model.Options.Search)) {
-                    int id;
-                    query = int.TryParse(model.Options.Search, out id) ?
-                        query.Where(o => o.Id == id) :
-                        query.Where(o => o.Customer.Contains(model.Options.Search));
-                }
-                var filterOption = model.Options.SelectedFilter;
-                if (!string.IsNullOrWhiteSpace(filterOption)) {
+            if (model.Options == null) {
+                model.Options = new ContentOptions {};
+            }
+            if (!string.IsNullOrWhiteSpace(model.Options.Search)) {
+                int id;
+                query = int.TryParse(model.Options.Search, out id) ?
+                    query.Where(o => o.Id == id) :
+                    query.Where(o => o.Customer.Contains(model.Options.Search));
+            }
+            var filterOption = model.Options.SelectedFilter;
+            if (string.IsNullOrWhiteSpace(filterOption) ||
+                filterOption.Equals("active", StringComparison.OrdinalIgnoreCase)) {
+                query = query.Where(
+                    o => o.Status != OrderPart.Archived &&
+                            o.Status != OrderPart.Cancelled);
+            }
+            else if (!filterOption.Equals("any", StringComparison.OrdinalIgnoreCase)) {
+                if (!states.Contains(filterOption)) return HttpNotFound();
+                query = query.Where(o => o.Status == filterOption);
+            }
 
-                    if (!states.Contains(filterOption))
-                        return HttpNotFound();
-
-                    query = query.Where(o => o.Status == filterOption);
-                }
-                else {
-                    query = query.Where(o => o.Status != OrderPart.Archived && o.Status != OrderPart.Cancelled);
-                }
-
-                switch (model.Options.OrderBy) {
-                    case ContentsOrder.Modified:
-                        query.OrderByDescending<CommonPartRecord>(cr => cr.ModifiedUtc);
-                        break;
-                    case ContentsOrder.Created:
-                        query.OrderByDescending<CommonPartRecord>(cr => cr.CreatedUtc);
-                        break;
-                }
-                model.Options.FilterOptions =
-                    _orderService.StatusLabels.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.Text));
+            switch (model.Options.OrderBy) {
+                case ContentsOrder.Modified:
+                    query.OrderByDescending<CommonPartRecord>(cr => cr.ModifiedUtc);
+                    break;
+                case ContentsOrder.Created:
+                    query.OrderByDescending<CommonPartRecord>(cr => cr.CreatedUtc);
+                    break;
             }
+            model.Options.FilterOptions =
+                _orderService.StatusLabels.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.Text));
 
             var pagerShape = Shape.Pager(pager).TotalItemCount(query.Count());
             var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList();
             var routeValues = ControllerContext.RouteData.Values;
             if (options != null) {
                 routeValues["Options.OrderBy"] = options.OrderBy;
-                if (OrderPart.States.Any(
+                if ((OrderPart.States.Union(new[] {"any", "active"})).Any(
                         ctd => string.Equals(ctd, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) {
                     routeValues["Options.SelectedFilter"] = options.SelectedFilter;
                 }

File Controllers/OrderSslController.cs

             _addressFormatter = addressFormatter;
         }
 
+        [OutputCache(NoStore = true, Duration = 0)]
         public ActionResult Confirmation() {
             if (!TempData.ContainsKey("OrderId")) {
                 return HttpNotFound();
             }
             var orderId = TempData["OrderId"];
             TempData.Keep("OrderId");
-            var order = _contentManager.Get<OrderPart>((int)orderId);
+            var order = _contentManager.Get<OrderPart>((int) orderId);
             var billingAddress = _addressFormatter.Format(order.BillingAddress);
             var shippingAddress = _addressFormatter.Format(order.ShippingAddress);
             var products = _contentManager
-                .GetMany<IContent>(order.Items.Select(p => p.ProductId), VersionOptions.Latest, QueryHints.Empty)
+                .GetMany<IContent>(
+                    order.Items.Select(p => p.ProductId).Distinct(),
+                    VersionOptions.Latest,
+                    QueryHints.Empty)
                 .ToDictionary(p => p.Id, p => p);
             var shape = _shapeFactory.Order_Confirmation(
                 OrderId: order.Id,
             return new ShapeResult(this, shape);
         }
 
+        [OutputCache(NoStore = true, Duration = 0)]
         public ActionResult Show(int id) {
             if (TempData.ContainsKey("OrderId")) {
                 return Confirmation();
             return RedirectToAction("Show", new {id});
         }
     }
-}
+}

File Controllers/ShoppingCartController.cs

         private readonly IEnumerable<IExtraCartInfoProvider> _extraCartInfoProviders;
         private readonly IWorkflowManager _workflowManager;
 
+        private const string AttributePrefix = "productattributes.a";
+
         public ShoppingCartController(
             IShoppingCart shoppingCart,
             IShapeFactory shapeFactory,
         }
 
         [HttpPost]
-        public ActionResult Add(int id, int quantity, IDictionary<int, string> productattributes = null) {
-            // Workaround MVC buggy behavior that won't correctly bind an empty dictionary
-            if (productattributes != null && productattributes.Count == 1 && productattributes.Values.First() == "__none__") {
-                productattributes = null;
-            }
+        public ActionResult Add(int id, int quantity) {
+            // Manually parse product attributes because of a breaking change
+            // in MVC 5 dictionary model binding
+            var form = HttpContext.Request.Form;
+            var productattributes = form.AllKeys
+                .Where(key => key.StartsWith(AttributePrefix))
+                .ToDictionary(
+                    key => int.Parse(key.Substring(AttributePrefix.Length)),
+                    key => form[key]);
 
             // Retrieve minimum order quantity
             var productPart = _contentManager.Get<ProductPart>(id);

File Controllers/StripeController.cs

 using Nwazet.Commerce.Services;
 using Nwazet.Commerce.ViewModels;
 using Orchard;
+using Orchard.Environment.Extensions;
+using Orchard.Localization;
+using Orchard.Logging;
 using Orchard.Themes;
+using Orchard.UI.Notify;
 using Orchard.Workflows.Services;
 using Orchard.Environment.Extensions;
 
         private readonly IOrderService _orderService;
         private readonly IWorkContextAccessor _wca;
         private readonly IWorkflowManager _workflowManager;
+        private readonly INotifier _notifier;
 
         public StripeController(
             IStripeService stripeService,
             IOrderService orderService,
             IWorkContextAccessor wca,
-            IWorkflowManager workflowManager) {
+            IWorkflowManager workflowManager,
+            INotifier notifier) {
 
             _stripeService = stripeService;
             _orderService = orderService;
             _wca = wca;
             _workflowManager = workflowManager;
+            _notifier = notifier;
+
+            Logger = NullLogger.Instance;
+            T = NullLocalizer.Instance;
         }
 
+        public ILogger Logger { get; set; }
+        public Localizer T { get; set; }
+
         [HttpPost]
         public ActionResult Checkout(string checkoutData) {
             var stripeData = _stripeService.DecryptCheckoutData(checkoutData);
             if (!String.IsNullOrWhiteSpace(back)) {
                 return RedirectToAction("Index", "ShoppingCart");
             }
-            GetCheckoutData(stripeData);
+            var checkoutData = GetCheckoutData(stripeData);
+            if (AnyEmptyString(
+                checkoutData.Email,
+                checkoutData.BillingAddress.FirstName,
+                checkoutData.BillingAddress.LastName,
+                checkoutData.BillingAddress.Address1,
+                checkoutData.BillingAddress.City,
+                checkoutData.ShippingAddress.FirstName,
+                checkoutData.ShippingAddress.LastName,
+                checkoutData.ShippingAddress.Address1,
+                checkoutData.ShippingAddress.City
+                )) {
+                return RedirectToAction("Ship");
+            }
             return RedirectToAction("Pay");
         }
 
-        public ActionResult Pay() {
+        public ActionResult Pay(string errorMessage = null) {
             _wca.GetContext().Layout.IsCartPage = true;
             var checkoutData = GetCheckoutData();
             if (checkoutData.CheckoutItems == null || !checkoutData.CheckoutItems.Any()) {
                 return RedirectToAction("Index", "ShoppingCart");
             }
             checkoutData.PublishableKey = _stripeService.GetSettings().PublishableKey;
+            if (!String.IsNullOrEmpty(errorMessage)) {
+                _notifier.Error(new LocalizedString(errorMessage));
+            }
             return View(checkoutData);
         }
 
             // Call Stripe to charge card
             var stripeCharge = _stripeService.Charge(stripeToken, total);
 
+            if (stripeCharge.Error != null) {
+                Logger.Error(stripeCharge.Error.Type + ": " + stripeCharge.Error.Message);
+                _workflowManager.TriggerEvent("OrderError", null,
+                    () => new Dictionary<string, object> {
+                        {"CheckoutError", stripeCharge.Error}
+                    });
+                if (stripeCharge.Error.Type == "card_error") {
+                    return Pay(stripeCharge.Error.Message);
+                }
+                throw new InvalidOperationException(stripeCharge.Error.Type + ": " + stripeCharge.Error.Message);
+            }
+            
             int userId = -1;
             var currentUser = _wca.GetContext().CurrentUser;
             if (currentUser != null) {
-                userId = currentUser.Id;
-            }
+                userId = currentUser.Id;            
+            }           
 
             var order = _orderService.CreateOrder(
                 stripeCharge,
                     {"Content", order},
                     {"Order", order}
                 });
+            order.LogActivity(OrderPart.Event, T("Order created.").Text);
 
             return RedirectToAction("Confirmation", "OrderSsl");
         }
                 if (updateModel.ShippingOption != null) {
                     checkoutData.ShippingOption = updateModel.ShippingOption;
                 }
-                if (updateModel.BillingAddress != null) {
-                    checkoutData.BillingAddress = updateModel.BillingAddress;
-                }
                 if (updateModel.ShippingAddress != null) {
+                    if (updateModel.BillingAddress != null) {
+                        checkoutData.BillingAddress = updateModel.BillingAddress;
+                        // We don't let billing country be different from shipping address
+                        // because that's a strong indicator of credit card fraud
+                        checkoutData.BillingAddress.Country =
+                            updateModel.ShippingAddress.Country;
+                    }
                     checkoutData.ShippingAddress = updateModel.ShippingAddress;
                 }
                 if (updateModel.Taxes != null) {
             TempData.Keep(NwazetStripeCheckout);
             return checkoutData;
         }
+
+        private bool AnyEmptyString(params string[] strings) {
+            return strings.Any(String.IsNullOrWhiteSpace);
+        }
     }
-}
+}

File Controllers/UspsAdminController.cs

 using System.Net;
 using System.Web.Mvc;
 using System.Xml.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Services;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 using Orchard.UI.Admin;
 

File Drivers/DiscountPartDriver.cs

 using System;
 using System.Globalization;
 using System.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.ViewModels;
 using Orchard;

File Drivers/GoogleCheckoutSettingsPartDriver.cs

-using System;
-using Nwazet.Commerce.Models;
-using Orchard.Caching;
-using Orchard.ContentManagement;
-using Orchard.ContentManagement.Drivers;
-using Orchard.ContentManagement.Handlers;
-using Orchard.Environment.Extensions;
-
-namespace Nwazet.Commerce.Drivers {
-    [OrchardFeature("Google.Checkout")]
-    public class GoogleCheckoutSettingsPartDriver : ContentPartDriver<GoogleCheckoutSettingsPart> {
-        private readonly ISignals _signals;
-
-        public GoogleCheckoutSettingsPartDriver(ISignals signals) {
-            _signals = signals;
-        }
-
-        protected override string Prefix { get { return "GoogleCheckoutSettings"; } }
-
-        protected override DriverResult Editor(GoogleCheckoutSettingsPart part, dynamic shapeHelper) {
-            return ContentShape("Parts_GoogleCheckout_Settings",
-                               () => shapeHelper.EditorTemplate(
-                                   TemplateName: "Parts/GoogleCheckoutSettings",
-                                   Model: part.Record,
-                                   Prefix: Prefix)).OnGroup("GoogleCheckout");
-        }
-
-        protected override DriverResult Editor(GoogleCheckoutSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
-            updater.TryUpdateModel(part.Record, Prefix, null, null);
-            _signals.Trigger("GoogleCheckout.Changed");
-            return Editor(part, shapeHelper);
-        }
-
-        protected override void Importing(GoogleCheckoutSettingsPart part, ImportContentContext context) {
-
-            var merchantId = context.Attribute(part.PartDefinition.Name, "MerchantId");
-            if (!String.IsNullOrWhiteSpace(merchantId)) {
-                part.MerchantId = merchantId;
-            }
-            var currency = context.Attribute(part.PartDefinition.Name, "Currency");
-            if (!String.IsNullOrWhiteSpace(currency)) {
-                part.Currency = currency;
-            }
-            var weightUnit = context.Attribute(part.PartDefinition.Name, "WeightUnit");
-            if (!String.IsNullOrWhiteSpace(weightUnit)) {
-                part.WeightUnit = weightUnit;
-            }
-            var analyticsId = context.Attribute(part.PartDefinition.Name, "AnalyticsId");
-            if (!String.IsNullOrWhiteSpace(analyticsId)) {
-                part.AnalyticsId = analyticsId;
-            }
-            var useSandboxAttribute = context.Attribute(part.PartDefinition.Name, "UseSandbox");
-            bool useSandbox;
-            if (Boolean.TryParse(useSandboxAttribute, out useSandbox)) {
-                part.UseSandbox = useSandbox;
-            }
-        }
-
-        protected override void Exporting(GoogleCheckoutSettingsPart part, ExportContentContext context) {
-            context.Element(part.PartDefinition.Name).SetAttributeValue("MerchantId", part.MerchantId);
-            context.Element(part.PartDefinition.Name).SetAttributeValue("Currency", part.Currency);
-            context.Element(part.PartDefinition.Name).SetAttributeValue("WeightUnit", part.WeightUnit);
-            context.Element(part.PartDefinition.Name).SetAttributeValue("AnalyticsId", part.AnalyticsId);
-            context.Element(part.PartDefinition.Name).SetAttributeValue("UseSandbox", part.UseSandbox.ToString().ToLower());
-        }
-    }
-}

File Drivers/OrderPartDriver.cs

 using System.Collections.Generic;
 using System.Linq;
 using System.Xml.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Permissions;
 using Nwazet.Commerce.Services;
 
             var contentManager = part.ContentItem.ContentManager;
             var products = contentManager
-                .GetMany<IContent>(part.Items.Select(p => p.ProductId), VersionOptions.Latest, QueryHints.Empty)
+                .GetMany<IContent>(
+                    part.Items.Select(p => p.ProductId).Distinct(),
+                    VersionOptions.Latest, QueryHints.Empty)
                 .ToDictionary(p => p.Id, p => p);
             var linkToTransaction = _checkoutServices
                 .Select(s => s.GetChargeAdminUrl(part.CreditCardCharge.TransactionId))

File Drivers/ProductPartDriver.cs

 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Services;
 using Orchard;

File Drivers/SizeBasedShippingMethodPartDriver.cs

 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Services;
 using Orchard.ContentManagement;

File Drivers/StateOrCountryTaxPartDriver.cs

-using Nwazet.Commerce.Helpers;
-using Nwazet.Commerce.Models;
+using Nwazet.Commerce.Models;
 using Orchard.ContentManagement;
 using Orchard.ContentManagement.Drivers;
 using Orchard.ContentManagement.Handlers;

File Drivers/StripeSettingsPartDriver.cs

-using System;
-using Nwazet.Commerce.Helpers;
-using Nwazet.Commerce.Models;
+using Nwazet.Commerce.Models;
 using Orchard.Caching;
 using Orchard.ContentManagement;
 using Orchard.ContentManagement.Drivers;

File Drivers/UspsSettingsPartDriver.cs

-using Nwazet.Commerce.Helpers;
-using Nwazet.Commerce.Models;
+using Nwazet.Commerce.Models;
 using Orchard.ContentManagement;
 using Orchard.ContentManagement.Drivers;
 using Orchard.ContentManagement.Handlers;

File Drivers/UspsShippingMethodPartDriver.cs

 using System.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Services;
 using Orchard.ContentManagement;

File Drivers/WeightBasedShippingMethodPartDriver.cs

 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Services;
 using Orchard.ContentManagement;

File Exceptions/StripeException.cs

 using System;
 using System.Net;
+using Newtonsoft.Json.Linq;
 
 namespace Nwazet.Commerce.Exceptions {
     public class StripeException : Exception {
         public StripeException(string message, Exception innerException) : base (message, innerException) {}
 
         public WebExceptionStatus Status { get; set; }
-        public WebResponse Response { get; set; }
+        public JObject Response { get; set; }
     }
 }

File Handlers/GoogleCheckoutSettingsPartHandler.cs

-using Nwazet.Commerce.Models;
-using Orchard.ContentManagement;
-using Orchard.Data;
-using Orchard.ContentManagement.Handlers;
-using Orchard.Environment.Extensions;
-using Orchard.Localization;
-
-namespace Nwazet.Commerce.Handlers {
-    [OrchardFeature("Google.Checkout")]
-    public class GoogleCheckoutSettingsPartHandler : ContentHandler {
-        public GoogleCheckoutSettingsPartHandler(IRepository<GoogleCheckoutSettingsPartRecord> repository) {
-            T = NullLocalizer.Instance;
-            Filters.Add(StorageFilter.For(repository));
-            Filters.Add(new ActivatingFilter<GoogleCheckoutSettingsPart>("Site"));
-        }
-
-        public Localizer T { get; set; }
-
-        protected override void GetItemMetadata(GetContentItemMetadataContext context) {
-            if (context.ContentItem.ContentType != "Site")
-                return;
-            base.GetItemMetadata(context);
-            context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Google Checkout")) {
-                Id = "GoogleCheckout",
-                Position = "4.2"
-            });
-        }
-    }
-}

File Helpers/InfosetHelper.cs

-using System;
-using System.Linq.Expressions;
-using System.Xml.Linq;
-using Orchard.ContentManagement;
-using Orchard.ContentManagement.FieldStorage.InfosetStorage;
-
-namespace Nwazet.Commerce.Helpers {
-    public static class InfosetHelper {
-        public static TProperty Retrieve<TProperty>(this ContentPart contentPart, string name) {
-            var infosetPart = contentPart.As<InfosetPart>();
-            var el = infosetPart == null
-                ? null
-                : infosetPart.Infoset.Element.Element(contentPart.GetType().Name);
-            return el == null ? default(TProperty) : el.Attr<TProperty>(name);
-        }
-
-        public static void Store<TProperty>(this ContentPart contentPart,
-            string name, TProperty value) {
-
-            var partName = contentPart.GetType().Name;
-
-            var infosetPart = contentPart.As<InfosetPart>();
-            var infoset = infosetPart.Infoset;
-            var partElement = infoset.Element.Element(partName);
-            if (partElement == null) {
-                partElement = new XElement(partName);
-                infoset.Element.Add(partElement);
-            }
-            partElement.Attr(name, value);
-        }
-
-        public static TProperty Retrieve<TPart, TRecord, TProperty>(this TPart contentPart,
-            Expression<Func<TRecord, TProperty>> targetExpression)
-            where TPart : ContentPart<TRecord> {
-
-            var getter = ReflectionHelper<TRecord>.GetGetter(targetExpression);
-            return contentPart.Retrieve(targetExpression, getter);
-        }
-
-        public static TProperty Retrieve<TPart, TRecord, TProperty>(this TPart contentPart,
-            Expression<Func<TRecord, TProperty>> targetExpression,
-            Delegate defaultExpression)
-            where TPart : ContentPart<TRecord> {
-
-            var propertyInfo = ReflectionHelper<TRecord>.GetPropertyInfo(targetExpression);
-            var name = propertyInfo.Name;
-
-            var infosetPart = contentPart.As<InfosetPart>();
-            var el = infosetPart == null
-                ? null
-                : infosetPart.Infoset.Element.Element(contentPart.GetType().Name);
-            if (el == null || el.Attribute(name) == null) {
-                // Property has never been stored. Get it from the default expression and store that.
-                var defaultValue = defaultExpression == null
-                    ? default(TProperty)
-                    : (TProperty)defaultExpression.DynamicInvoke(contentPart.Record);
-                contentPart.Store(name, defaultValue);
-                return defaultValue;
-            }
-            return el.Attr<TProperty>(name);
-        }
-
-        public static void Store<TPart, TRecord, TProperty>(this TPart contentPart,
-            Expression<Func<TRecord, TProperty>> targetExpression,
-            TProperty value)
-            where TPart : ContentPart<TRecord> {
-            
-            var propertyInfo = ReflectionHelper<TRecord>.GetPropertyInfo(targetExpression);
-            var name = propertyInfo.Name;
-            propertyInfo.SetValue(contentPart.Record, value);
-            contentPart.Store(name, value);
-        }
-    }
-}

File Helpers/ReflectionHelper.cs

-using System;
-using System.Collections.Concurrent;
-using System.Linq.Expressions;
-using System.Reflection;
-
-namespace Nwazet.Commerce.Helpers {
-    public class ReflectionHelper<T> {
-        private static readonly ConcurrentDictionary<string, Delegate> _getterCache =
-            new ConcurrentDictionary<string, Delegate>();
-
-        public delegate TProperty PropertyGetterDelegate<out TProperty>(T target);
-
-        /// <summary>
-        /// Gets property info out of a Lambda.
-        /// </summary>
-        /// <typeparam name="TProperty">The return type of the Lambda.</typeparam>
-        /// <param name="expression">The Lambda expression.</param>
-        /// <returns>The property info.</returns>
-        public static PropertyInfo GetPropertyInfo<TProperty>(Expression<Func<T, TProperty>> expression) {
-            var memberExpression = expression.Body as MemberExpression;
-            if (memberExpression == null) {
-                throw new InvalidOperationException("Expression is not a member expression.");
-            }
-            var propertyInfo = memberExpression.Member as PropertyInfo;
-            if (propertyInfo == null) {
-                throw new InvalidOperationException("Expression is not for a property.");
-            }
-            return propertyInfo;
-        }
-
-        /// <summary>
-        /// Gets a delegate from a property expression.
-        /// </summary>
-        /// <typeparam name="TProperty">The type of the property.</typeparam>
-        /// <param name="targetExpression">The property expression.</param>
-        /// <returns>The delegate.</returns>
-        public static PropertyGetterDelegate<TProperty> GetGetter<TProperty>(
-            Expression<Func<T, TProperty>> targetExpression) {
-
-            var propertyInfo = GetPropertyInfo(targetExpression);
-            return (PropertyGetterDelegate<TProperty>) _getterCache
-                .GetOrAdd(propertyInfo.Name,
-                    s => propertyInfo.GetGetMethod()
-                        .CreateDelegate(typeof (PropertyGetterDelegate<TProperty>)));
-        }
-    }
-}

File Helpers/XmlHelper.cs

-using System;
-using System.Globalization;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Xml;
-using System.Xml.Linq;
-
-namespace Nwazet.Commerce.Helpers {
-    public static class XmlHelper {
-        /// <summary>
-        /// Like Add, but chainable.
-        /// </summary>
-        /// <param name="el">The parent element.</param>
-        /// <param name="children">The elements to add.</param>
-        /// <returns>Itself</returns>
-        public static XElement AddEl(this XElement el, params XElement[] children) {
-            el.Add(children.Cast<object>());
-            return el;
-        }
-
-        /// <summary>
-        /// Gets the string value of an attribute, and null if the attribute doesn't exist.
-        /// </summary>
-        /// <param name="el">The element.</param>
-        /// <param name="name">The name of the attribute.</param>
-        /// <returns>The string value of the attribute if it exists, null otherwise.</returns>
-        public static string Attr(this XElement el, string name) {
-            var attr = el.Attribute(name);
-            return attr == null ? null : attr.Value;
-        }
-
-        /// <summary>
-        /// Gets a typed value from an attribute.
-        /// </summary>
-        /// <typeparam name="T">The type of the value</typeparam>
-        /// <param name="el">The element.</param>
-        /// <param name="name">The name of the attribute.</param>
-        /// <returns>The attribute value</returns>
-        public static T Attr<T>(this XElement el, string name) {
-
-            var attr = el.Attribute(name);
-            return attr == null ? default(T) : Parse<T>(attr.Value);
-        }
-
-        /// <summary>
-        /// Sets an attribute value. This is chainable.
-        /// </summary>
-        /// <typeparam name="T">The type of the value.</typeparam>
-        /// <param name="el">The element.</param>
-        /// <param name="name">The attribute name.</param>
-        /// <param name="value">The value to set.</param>
-        /// <returns>Itself</returns>
-        public static XElement Attr<T>(this XElement el, string name, T value) {
-            el.SetAttributeValue(name, ToString(value));
-            return el;
-        }
-
-        /// <summary>
-        /// Returns the text contents of a child element.
-        /// </summary>
-        /// <param name="el">The parent element.</param>
-        /// <param name="name">The name of the child element.</param>
-        /// <returns>The text for the child element, and null if it doesn't exist.</returns>
-        public static string El(this XElement el, string name) {
-            var childElement = el.Element(name);
-            return childElement == null ? null : childElement.Value;
-        }
-
-        /// <summary>
-        /// Creates and sets the value of a child element. This is chainable.
-        /// </summary>
-        /// <typeparam name="T">The type of the value.</typeparam>
-        /// <param name="el">The parent element.</param>
-        /// <param name="name">The name of the child element.</param>
-        /// <param name="value">The value to set.</param>
-        /// <returns>Itself</returns>
-        public static XElement El<T>(this XElement el, string name, T value) {
-            el.SetElementValue(name, value);
-            return el;
-        }
-
-        /// <summary>
-        /// Sets a property value from an attribute of the same name.
-        /// </summary>
-        /// <typeparam name="TTarget">The type of the target object.</typeparam>
-        /// <typeparam name="TProperty">The type of the target property</typeparam>
-        /// <param name="el">The element.</param>
-        /// <param name="target">The target object.</param>
-        /// <param name="targetExpression">The property expression.</param>
-        /// <returns>Itself</returns>
-        public static XElement FromAttr<TTarget, TProperty>(this XElement el, TTarget target,
-            Expression<Func<TTarget, TProperty>> targetExpression) {
-
-            if (target == null) return el;
-            var propertyInfo = ReflectionHelper<TTarget>.GetPropertyInfo(targetExpression);
-            var name = propertyInfo.Name;
-            var attr = el.Attribute(name);
-
-            if (attr == null) return el;
-            propertyInfo.SetValue(target, el.Attr<TProperty>(name), null);
-            return el;
-        }
-
-        /// <summary>
-        /// Sets an attribute with the value of a property of the same name.
-        /// </summary>
-        /// <typeparam name="TTarget">The type of the object.</typeparam>
-        /// <typeparam name="TProperty">The type of the property.</typeparam>
-        /// <param name="el">The element.</param>
-        /// <param name="target">The object.</param>
-        /// <param name="targetExpression">The property expression.</param>
-        /// <returns>Itself</returns>
-        public static XElement ToAttr<TTarget, TProperty>(this XElement el, TTarget target,
-            Expression<Func<TTarget, TProperty>> targetExpression) {
-
-            if (target == null) return el;
-            var propertyInfo = ReflectionHelper<TTarget>.GetPropertyInfo(targetExpression);
-            var name = propertyInfo.Name;
-            var val = (TProperty) propertyInfo.GetValue(target, null);
-
-            el.Attr(name, ToString(val));
-            return el;
-        }
-
-        /// <summary>
-        /// Gets the text value of an element as the specified type.
-        /// </summary>
-        /// <typeparam name="TValue">The type to parse the element as.</typeparam>
-        /// <param name="el">The element.</param>
-        /// <returns>The value of the element as type TValue.</returns>
-        public static TValue Val<TValue>(this XElement el) {
-            return Parse<TValue>(el.Value);
-        }
-
-        /// <summary>
-        /// Sets the value of an element.
-        /// </summary>
-        /// <typeparam name="TValue">The type of the value to set.</typeparam>
-        /// <param name="el">The element.</param>
-        /// <param name="value">The value.</param>
-        /// <returns>The element.</returns>
-        public static XElement Val<TValue>(this XElement el, TValue value) {
-            el.SetValue(ToString(value));
-            return el;
-        }
-
-        /// <summary>
-        /// Serializes the provided value as a string.
-        /// </summary>
-        /// <typeparam name="T">The type of the value.</typeparam>
-        /// <param name="value">The value.</param>
-        /// <returns>The string representation of the value.</returns>
-        public static string ToString<T>(T value) {
-            var type = typeof (T);
-            if (type == typeof (string)) {
-                return Convert.ToString(value);
-            }
-            if ((!type.IsValueType || Nullable.GetUnderlyingType(type) != null) &&
-                value == null &&
-                type != typeof (string)) {
-
-                return "null";
-            }
-
-            if (type == typeof (DateTime) || type == typeof (DateTime?)) {
-                return XmlConvert.ToString(Convert.ToDateTime(value),
-                    XmlDateTimeSerializationMode.Utc);
-            }
-
-            if (type == typeof (bool) ||
-                type == typeof (bool?)) {
-                return Convert.ToBoolean(value) ? "true" : "false";
-            }
-
-            if (type == typeof (int) ||
-                type == typeof (int?)) {
-
-                return Convert.ToInt64(value).ToString(CultureInfo.InvariantCulture);
-            }
-
-            if (type == typeof (double) ||
-                type == typeof (double?)) {
-
-                var doubleValue = (double) (object) value;
-                if (double.IsPositiveInfinity(doubleValue)) {
-                    return "infinity";
-                }
-                if (double.IsNegativeInfinity(doubleValue)) {
-                    return "-infinity";
-                }
-                return doubleValue.ToString(CultureInfo.InvariantCulture);
-            }
-
-            if (type == typeof (float) ||
-                type == typeof (float?)) {
-
-                var floatValue = (float) (object) value;
-                if (float.IsPositiveInfinity(floatValue)) {
-                    return "infinity";
-                }
-                if (float.IsNegativeInfinity(floatValue)) {
-                    return "-infinity";
-                }
-                return floatValue.ToString(CultureInfo.InvariantCulture);
-            }
-
-            if (type == typeof (decimal) ||
-                type == typeof (decimal?)) {
-
-                var decimalValue = Convert.ToDecimal(value);
-                return decimalValue.ToString(CultureInfo.InvariantCulture);
-            }
-
-            throw new NotSupportedException(String.Format("Could not handle type {0}", type.Name));
-        }
-
-        /// <summary>
-        /// Parses a string value as the provided type.
-        /// </summary>
-        /// <typeparam name="T">The destination type</typeparam>
-        /// <param name="value">The string representation of the value to parse.</param>
-        /// <returns>The parsed value with type T.</returns>
-        public static T Parse<T>(string value) {
-            var type = typeof (T);
-
-            if (type == typeof (string)) {
-                return (T) (object) value;
-            }
-            if (value == null ||
-                "null".Equals(value, StringComparison.Ordinal) &&
-                ((!type.IsValueType || Nullable.GetUnderlyingType(type) != null))) {
-
-                return default(T);
-            }
-
-            if ("infinity".Equals(value, StringComparison.Ordinal)) {
-                if (type == typeof (float) || type == typeof (float?)) return (T) (object) float.PositiveInfinity;
-                if (type == typeof (double) || type == typeof (double?)) return (T) (object) double.PositiveInfinity;
-                throw new NotSupportedException(String.Format("Infinity not supported for type {0}", type.Name));
-            }
-            if ("-infinity".Equals(value, StringComparison.Ordinal)) {
-                if (type == typeof (float)) return (T) (object) float.NegativeInfinity;
-                if (type == typeof (double)) return (T) (object) double.NegativeInfinity;
-                throw new NotSupportedException(String.Format("Infinity not supported for type {0}", type.Name));
-            }
-            if (type == typeof (int) || type == typeof (int?)) {
-                return (T) (object) int.Parse(value, CultureInfo.InvariantCulture);
-            }
-            if (type == typeof (bool) || type == typeof (bool?)) {
-                return (T) (object) value.Equals("true", StringComparison.Ordinal);
-            }
-            if (type == typeof (DateTime) || type == typeof (DateTime?)) {
-                return (T) (object) XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc);
-            }
-            if (type == typeof (double) || type == typeof (double?)) {
-                return (T) (object) double.Parse(value, CultureInfo.InvariantCulture);
-            }
-            if (type == typeof (float) || type == typeof (float?)) {
-                return (T) (object) float.Parse(value, CultureInfo.InvariantCulture);
-            }
-            if (type == typeof (decimal) || type == typeof (decimal?)) {
-                return (T) (object) decimal.Parse(value, CultureInfo.InvariantCulture);
-            }
-            throw new NotSupportedException(String.Format("Could not handle type {0}", type.Name));
-        }
-
-        /// <summary>
-        /// Gives context to an XElement, enabling chained property operations.
-        /// </summary>
-        /// <typeparam name="TContext">The type of the context.</typeparam>
-        /// <param name="el">The element.</param>
-        /// <param name="context">The context.</param>
-        /// <returns>The element with context.</returns>
-        public static XElementWithContext<TContext> With<TContext>(this XElement el, TContext context) {
-            return new XElementWithContext<TContext>(el, context);
-        }
-
-        /// <summary>
-        /// A wrapper for XElement, with context, for strongly-typed manipulation
-        /// of an XElement.
-        /// </summary>
-        /// <typeparam name="TContext">The type of the context.</typeparam>
-        public class XElementWithContext<TContext> {
-            public XElementWithContext(XElement element, TContext context) {
-                Element = element;
-                Context = context;
-            }
-
-            public XElement Element { get; private set; }
-            public TContext Context { get; private set; }
-
-            public static implicit operator XElement(XElementWithContext<TContext> elementWithContext) {
-                return elementWithContext.Element;
-            }
-
-            /// <summary>
-            /// Replaces the current context with a new one, enabling chained action on different objects.
-            /// </summary>
-            /// <typeparam name="TNewContext">The type of the new context.</typeparam>
-            /// <param name="context">The new context.</param>
-            /// <returns>A new XElementWithContext, that has the new context.</returns>
-            public XElementWithContext<TNewContext> With<TNewContext>(TNewContext context) {
-                return new XElementWithContext<TNewContext>(Element, context);
-            }
-
-            /// <summary>
-            /// Sets the value of a context property as an attribute of the same name on the element.
-            /// </summary>
-            /// <typeparam name="TProperty">The type of the property.</typeparam>
-            /// <param name="targetExpression">The property expression.</param>
-            /// <returns>Itself</returns>
-            public XElementWithContext<TContext> ToAttr<TProperty>(
-                Expression<Func<TContext, TProperty>> targetExpression) {
-                Element.ToAttr(Context, targetExpression);
-                return this;
-            }
-
-            /// <summary>
-            /// Gets an attribute on the element and sets the property of the same name on the context with its value.
-            /// </summary>
-            /// <typeparam name="TProperty">The type of the property.</typeparam>
-            /// <param name="targetExpression">The property expression.</param>
-            /// <returns>Itself</returns>
-            public XElementWithContext<TContext> FromAttr<TProperty>(
-                Expression<Func<TContext, TProperty>> targetExpression) {
-                Element.FromAttr(Context, targetExpression);
-                return this;
-            }
-
-            /// <summary>
-            /// Evaluates an attribute from an expression.
-            /// It's a nice strongly-typed way to read attributes.
-            /// </summary>
-            /// <typeparam name="TProperty">The type of the property.</typeparam>
-            /// <param name="expression">The property expression.</param>
-            /// <returns>The attribute, ready to be cast.</returns>
-            public TProperty Attr<TProperty>(Expression<Func<TContext, TProperty>> expression) {
-                var propertyInfo = ReflectionHelper<TContext>.GetPropertyInfo(expression);
-                var name = propertyInfo.Name;
-                return Element.Attr<TProperty>(name);
-            }
-        }
-    }
-}

File Migrations/GoogleCheckoutMigrations.cs

-using Orchard.Data.Migration;
-using Orchard.Environment.Extensions;
-
-namespace Nwazet.Commerce.Migrations {
-    [OrchardFeature("Google.Checkout")]
-    public class GoogleCheckoutMigrations : DataMigrationImpl {
-
-        public int Create() {
-            SchemaBuilder.CreateTable("GoogleCheckoutSettingsPartRecord", table => table
-                .ContentPartRecord()
-                .Column<string>("MerchantId")
-                .Column<string>("AnalyticsId")
-                .Column<string>("Currency", column => column.WithDefault("USD"))
-                .Column<string>("WeightUnit", column => column.WithDefault("LB"))
-                .Column<bool>("UseSandbox", column => column.WithDefault(true))
-            );
-
-            return 1;
-        }
-    }
-}

File Models/Address.cs

 using System;
 using System.Xml.Linq;
-using Nwazet.Commerce.Helpers;
+using Orchard.ContentManagement;
 
 namespace Nwazet.Commerce.Models {
     public class Address {

File Models/CheckoutError.cs

+namespace Nwazet.Commerce.Models {
+    public class CheckoutError {
+        public string Type { get; set; }
+        public string Message { get; set; }
+        public string Code { get; set; }
+    }
+}

File Models/CreditCardCharge.cs

         public string Last4 { get; set; }
         public int ExpirationMonth { get; set; }
         public int ExpirationYear { get; set; }
+        public CheckoutError Error { get; set; }
 
         public override string ToString() {
             return "**** **** **** " + Last4 + " " + ExpirationMonth + "/" + ExpirationYear;

File Models/DiscountPart.cs

 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Promotions")]
-    public class DiscountPart : InfosetContentPart<DiscountPartRecord> {
+    public class DiscountPart : ContentPart<DiscountPartRecord> {
         public string Name { get { return Record.Name; } set { Record.Name = value; } }
 
         public double? DiscountPercent {

File Models/GoogleCheckoutSettingsPart.cs

-using System.ComponentModel;
-using System.ComponentModel.DataAnnotations;
-using Orchard.ContentManagement;
-using Orchard.Environment.Extensions;
-
-namespace Nwazet.Commerce.Models {
-    [OrchardFeature("Google.Checkout")]
-    public class GoogleCheckoutSettingsPart : ContentPart<GoogleCheckoutSettingsPartRecord> {
-        [Required]
-        public string MerchantId { get { return Record.MerchantId; } set { Record.MerchantId = value; } }
-        public bool UseSandbox { get { return Record.UseSandbox; } set { Record.UseSandbox = value; } }
-        [DefaultValue("USD")]
-        public string Currency { get { return Record.Currency; } set { Record.Currency = value; } }
-        [DefaultValue("LB")]
-        public string WeightUnit { get { return Record.WeightUnit; } set { Record.WeightUnit = value; } }
-        public string AnalyticsId { get { return Record.AnalyticsId; } set { Record.AnalyticsId = value; } }
-    }
-}

File Models/GoogleCheckoutSettingsPartRecord.cs

-using Orchard.ContentManagement.Records;
-using Orchard.Environment.Extensions;
-
-namespace Nwazet.Commerce.Models {
-    [OrchardFeature("Google.Checkout")]
-    public class GoogleCheckoutSettingsPartRecord : ContentPartRecord {
-        public virtual string MerchantId { get; set; }
-        public virtual bool UseSandbox { get; set; }
-        public virtual string Currency { get; set; }
-        public virtual string WeightUnit { get; set; }
-        public virtual string AnalyticsId { get; set; }
-    }
-}

File Models/InfosetContentPart.cs

-using System;
-using System.Linq.Expressions;
-using Nwazet.Commerce.Helpers;
-using Orchard.ContentManagement;
-
-namespace Nwazet.Commerce.Models {
-    // These base classes won't be necessary any more once
-    // their methods are integrated into core's ContentPart and ContentPart<TRecord>.
-    public class InfosetContentPart<TRecord> : ContentPart<TRecord> {
-
-        protected TProperty Retrieve<TProperty>(Expression<Func<TRecord, TProperty>> targetExpression) {
-            return InfosetHelper.Retrieve(this, targetExpression);
-        }
-
-        protected TProperty Retrieve<TProperty>(
-            Expression<Func<TRecord, TProperty>> targetExpression,
-            Func<TRecord, TProperty> defaultExpression) {
-
-            return InfosetHelper.Retrieve(this, targetExpression, defaultExpression);
-        }
-
-        protected InfosetContentPart<TRecord> Store<TProperty>(
-            Expression<Func<TRecord, TProperty>> targetExpression,
-            TProperty value) {
-
-            InfosetHelper.Store(this, targetExpression, value);
-            return this;
-        }
-    }
-}

File Models/OrderPart.cs

 using System.Collections.Generic;
 using System.Linq;
 using System.Xml.Linq;
-using Nwazet.Commerce.Helpers;
 using Orchard.ContentManagement;
 using Orchard.ContentManagement.Aspects;
 using Orchard.Environment.Extensions;

File Models/ProductAttributePart.cs

 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Attributes")]
-    public class ProductAttributePart : InfosetContentPart<ProductAttributePartRecord> {
+    public class ProductAttributePart : ContentPart<ProductAttributePartRecord> {
         public IEnumerable<string> AttributeValues {
             get {
                 var values = AttributeValuesString;

File Models/ProductAttributesPart.cs

 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Attributes")]
-    public class ProductAttributesPart : InfosetContentPart<ProductAttributesPartRecord> {
+    public class ProductAttributesPart : ContentPart<ProductAttributesPartRecord> {
         public IEnumerable<int> AttributeIds {
             get {
                 var attributes = Retrieve(r => r.Attributes);

File Models/ProductPart.cs

 using System.ComponentModel.DataAnnotations;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Commerce")]
-    public class ProductPart : InfosetContentPart<ProductPartRecord>, IProduct {
+    public class ProductPart : ContentPart<ProductPartRecord>, IProduct {
         [Required]
         public string Sku {
             get { return Retrieve(r => r.Sku); }

File Models/SizeBasedShippingMethodPart.cs

 using System.Linq;
 using Nwazet.Commerce.Services;
 using Orchard;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Shipping")]
-    public class SizeBasedShippingMethodPart : InfosetContentPart<SizeBasedShippingMethodPartRecord>, IShippingMethod {
+    public class SizeBasedShippingMethodPart : ContentPart<SizeBasedShippingMethodPartRecord>, IShippingMethod {
         public string Name {
             get { return Retrieve(r => r.Name); }
             set { Store(r => r.Name, value); }

File Models/StateOrCountryTaxPart.cs

 using System;
 using System.Collections.Generic;
 using Nwazet.Commerce.Services;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Taxes")]
-    public class StateOrCountryTaxPart : InfosetContentPart<StateOrCountryTaxPartRecord>, ITax {
+    public class StateOrCountryTaxPart : ContentPart<StateOrCountryTaxPartRecord>, ITax {
         public string State {
             get { return Retrieve(r => r.State); }
             set { Store(r => r.State, value); }

File Models/StripeSettingsPart.cs

 using System.ComponentModel.DataAnnotations;
-using Nwazet.Commerce.Helpers;
 using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
     public class StripeSettingsPart : ContentPart {
         [Required]
         public string PublishableKey {
-            get { return this.Retrieve<string>("PublishableKey"); }
-            set { this.Store("PublishableKey", value); }
+            get { return this.Retrieve(p => p.PublishableKey); }
+            set { this.Store(p => p.PublishableKey, value); }
         }
 
         public string SecretKey {
-            get { return this.Retrieve<string>("SecretKey"); }
-            set { this.Store("SecretKey", value); }
+            get { return this.Retrieve(p => p.SecretKey); }
+            set { this.Store(p => p.SecretKey, value); }
         }
 
         public string Currency {
-            get { return this.Retrieve<string>("Currency"); }
-            set { this.Store("Currency", value); }
+            get { return this.Retrieve(p => p.Currency); }
+            set { this.Store(p => p.Currency, value); }
         }
     }
 }

File Models/UspsSettingsPart.cs

-using Nwazet.Commerce.Helpers;
-using Orchard.ContentManagement;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Usps.Shipping")]
     public class UspsSettingsPart : ContentPart {
         public string UserId {
-            get { return this.Retrieve<string>("UserId"); } 
-            set { this.Store("UserId", value); }
+            get { return this.Retrieve(p => p.UserId); } 
+            set { this.Store(p => p.UserId, value); }
         }
 
         public string OriginZip {
-            get { return this.Retrieve<string>("OriginZip"); }
-            set { this.Store("OriginZip", value); }
+            get { return this.Retrieve(p => p.OriginZip); }
+            set { this.Store(p => p.OriginZip, value); }
         }
 
         public bool CommercialPrices {
-            get { return this.Retrieve<bool>("CommercialPrices"); }
-            set { this.Store("CommercialPrices", value); }
+            get { return this.Retrieve(p => p.CommercialPrices); }
+            set { this.Store(p => p.CommercialPrices, value); }
         }
 
         public bool CommercialPlusPrices {
-            get { return this.Retrieve<bool>("CommercialPlusPrices"); }
-            set { this.Store("CommercialPlusPrices", value); }
+            get { return this.Retrieve(p => p.CommercialPlusPrices); }
+            set { this.Store(p => p.CommercialPlusPrices, value); }
         }
     }
 }

File Models/UspsShippingMethodPart.cs

 using System.Linq;
 using Nwazet.Commerce.Services;
 using Orchard;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Usps.Shipping")]
-    public class UspsShippingMethodPart : InfosetContentPart<UspsShippingMethodPartRecord>, IShippingMethod {
+    public class UspsShippingMethodPart : ContentPart<UspsShippingMethodPartRecord>, IShippingMethod {
         public string Name {
             get { return Retrieve(r => r.Name); }
             set { Store(r => r.Name, value); }

File Models/WeightBasedShippingMethodPart.cs

 using System.Linq;
 using Nwazet.Commerce.Services;
 using Orchard;
+using Orchard.ContentManagement;
 using Orchard.Environment.Extensions;
 
 namespace Nwazet.Commerce.Models {
     [OrchardFeature("Nwazet.Shipping")]
-    public class WeightBasedShippingMethodPart : InfosetContentPart<WeightBasedShippingMethodPartRecord>,
+    public class WeightBasedShippingMethodPart : ContentPart<WeightBasedShippingMethodPartRecord>,
         IShippingMethod {
         public string Name {
             get { return Retrieve(r => r.Name); }
         Description: Stripe checkout for Nwazet.Commerce
         Category: Commerce
         Dependencies: Nwazet.Commerce, Nwazet.Orders
-    Google.Checkout:
-        Name: Google Checkout (deprecated)
-        Description: Google Checkout part that can be added to any content type. This feature is deprecated as Google is retiring this service.
-        Category: Commerce
-        Dependencies: Nwazet.Commerce, Nwazet.Shipping

File Nwazet.Commerce.Tests/InfosetHelperTests.cs

-using NUnit.Framework;
-using Nwazet.Commerce.Models;
-using Nwazet.Commerce.Tests.Helpers;
-using Orchard.ContentManagement;
-using Nwazet.Commerce.Helpers;
-using Orchard.ContentManagement.FieldStorage.InfosetStorage;
-using Orchard.ContentManagement.Records;
-
-namespace Nwazet.Commerce.Tests {
-    public class InfosetHelperTests {
-        [Test]
-        public void StoreByNameSavesIntoInfoset() {
-            var part = new TestPart();
-            ContentHelpers.PreparePart(part, "Test");
-            part.Foo = 42;
-            var infosetXml = part.As<InfosetPart>().Infoset.Element;
-            var testPartElement = infosetXml.Element(typeof (TestPart).Name);
-            Assert.That(testPartElement, Is.Not.Null);
-            var fooAttribute = testPartElement.Attr<int>("Foo");
-
-            Assert.That(part.Foo, Is.EqualTo(42));
-            Assert.That(fooAttribute, Is.EqualTo(42));
-        }
-
-        [Test]
-        public void RetrieveSavesIntoInfoset() {
-            var part = new TestPartWithRecord();
-            ContentHelpers.PreparePart<TestPartWithRecord, TestPartWithRecordRecord>(part, "Test");
-            part.Record.Foo = 42;
-            var infosetXml = part.As<InfosetPart>().Infoset.Element;
-            var testPartElement = infosetXml.Element(typeof (TestPartWithRecord).Name);
-            Assert.That(testPartElement, Is.Null);
-
-            var foo = part.Foo;
-            testPartElement = infosetXml.Element(typeof(TestPartWithRecord).Name);
-            Assert.That(testPartElement, Is.Not.Null);
-            var fooAttribute = testPartElement.Attr<int>("Foo");
-
-            Assert.That(foo, Is.EqualTo(42));
-            Assert.That(fooAttribute, Is.EqualTo(42));
-        }
-
-        public class TestPart : ContentPart {
-            public int Foo {
-                get { return this.Retrieve<int>("Foo"); }
-                set { this.Store("Foo", value); }
-            }
-        }
-
-        public class TestPartWithRecordRecord : ContentPartRecord {
-            public virtual int Foo { get; set; }
-        }
-
-        public class TestPartWithRecord : InfosetContentPart<TestPartWithRecordRecord> {
-            public int Foo {
-                get { return Retrieve(r => r.Foo); }
-                set { Store(r => r.Foo, value); }
-            }
-        }
-    }
-}

File Nwazet.Commerce.Tests/Nwazet.Commerce.Tests.csproj

     <Compile Include="Helpers\ContentHelpers.cs" />
     <Compile Include="Helpers\ShippingHelpers.cs" />
     <Compile Include="Helpers\ShoppingCartHelpers.cs" />
-    <Compile Include="InfosetHelperTests.cs" />
     <Compile Include="ShippingServiceTest.cs" />
     <Compile Include="TaxTests.cs" />
     <Compile Include="UspsServiceInternationalTests.cs" />
     <Compile Include="UspsShippingMethodPartDriverTests.cs" />
     <Compile Include="WeightBasedShippingMethodTest.cs" />
     <Compile Include="Stubs\WorkContextAccessorStub.cs" />
-    <Compile Include="XmlHelperTests.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\..\..\Orchard\Orchard.Framework.csproj">

File Nwazet.Commerce.Tests/UspsShippingMethodPartDriverTests.cs

 using System.Xml.Linq;
 using NUnit.Framework;
 using Nwazet.Commerce.Drivers;
-using Nwazet.Commerce.Helpers;
 using Nwazet.Commerce.Models;
 using Nwazet.Commerce.Tests.Helpers;
 using Orchard.ContentManagement;

File Nwazet.Commerce.Tests/XmlHelperTests.cs

-using System;
-using System.Linq;
-using System.Xml.Linq;
-using NUnit.Framework;
-using Nwazet.Commerce.Helpers;
-
-namespace Nwazet.Commerce.Tests {
-    [TestFixture]
-    public class XmlHelperTests {
-        [Test]
-        public void AddEl() {
-            var el = new XElement("data");
-            el
-                .AddEl(new XElement("node1"), new XElement("node2"))
-                .AddEl(new XElement("node3"));
-
-            Assert.That(el.Descendants().Count(), Is.EqualTo(3));
-            Assert.That(el.Descendants().First().Name.ToString(), Is.EqualTo("node1"));
-            Assert.That(el.Descendants().ElementAt(1).Name.ToString(), Is.EqualTo("node2"));
-            Assert.That(el.Descendants().ElementAt(2).Name.ToString(), Is.EqualTo("node3"));
-        }
-
-        [Test]
-        public void Val() {
-            var el = new XElement("data");
-            el = el.Val(123);
-            var val = el.Val<int>();
-
-            Assert.That(val, Is.EqualTo(123));
-            Assert.That(el.ToString(SaveOptions.DisableFormatting),
-                Is.EqualTo("<data>123</data>"));
-        }
-
-        [Test]
-        public void Infinities() {
-            var el = new XElement("data")
-                .Attr("doubleplus", double.PositiveInfinity)
-                .Attr("doubleminus", double.NegativeInfinity)
-                .Attr("floatplus", float.PositiveInfinity)
-                .Attr("floatminus", float.NegativeInfinity);
-
-            Assert.That(el.Attr<string>("doubleplus"), Is.EqualTo("infinity"));
-            Assert.That(el.Attr<string>("doubleminus"), Is.EqualTo("-infinity"));
-            Assert.That(el.Attr<string>("floatplus"), Is.EqualTo("infinity"));
-            Assert.That(el.Attr<string>("floatminus"), Is.EqualTo("-infinity"));
-
-            Assert.That(double.IsPositiveInfinity(el.Attr<double>("doubleplus")), Is.True);
-            Assert.That(double.IsNegativeInfinity(el.Attr<double>("doubleminus")), Is.True);
-            Assert.That(double.IsPositiveInfinity(el.Attr<float>("floatplus")), Is.True);
-            Assert.That(double.IsNegativeInfinity(el.Attr<float>("floatminus")), Is.True);
-        }
-
-        [Test]
-        public void StringToAttribute() {
-            var el = new XElement("data");
-            el.Attr("foo", "bar");
-
-            Assert.That(el.Attribute("foo").Value, Is.EqualTo("bar"));
-        }
-
-        [Test]
-        public void IntToAttribute() {
-            var el = new XElement("data");
-            el.Attr("foo", 42);
-
-            Assert.That(el.Attribute("foo").Value, Is.EqualTo("42"));
-        }
-
-        [Test]
-        public void BoolToAttribute() {
-            var el = new XElement("data");
-            el.Attr("foo", true);
-            el.Attr("bar", false);
-
-            Assert.That(el.Attribute("foo").Value, Is.EqualTo("true"));
-            Assert.That(el.Attribute("bar").Value, Is.EqualTo("false"));
-        }
-
-        [Test]
-        public void DateTimeToAttribute() {
-            var el = new XElement("data");
-            el.Attr("foo", new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc));
-
-            Assert.That(el.Attribute("foo").Value, Is.EqualTo("1970-05-21T13:55:21.934Z"));
-        }
-
-        [Test]
-        public void DoubleFloatDecimalToAttribute() {
-            var el = new XElement("data");
-            el.Attr("double", 12.456D);
-            el.Attr("float", 12.457F);
-            el.Attr("decimal", 12.458M);
-
-            Assert.That(el.Attribute("double").Value, Is.EqualTo("12.456"));
-            Assert.That(el.Attribute("float").Value, Is.EqualTo("12.457"));
-            Assert.That(el.Attribute("decimal").Value, Is.EqualTo("12.458"));
-        }
-
-        [Test]
-        public void ReadAttribute() {
-            var el = XElement.Parse("<data foo=\"bar\"/>");
-
-            Assert.That(el.Attr("foo"), Is.EqualTo("bar"));
-            Assert.That(el.Attr("bar"), Is.Null);
-        }
-
-        [Test]
-        public void StringToElement() {
-            var el = new XElement("data");
-            el.El("foo", "bar");
-
-            Assert.That(el.Element("foo").Value, Is.EqualTo("bar"));
-        }
-
-        [Test]
-        public void IntToElement() {
-            var el = new XElement("data");
-            el.El("foo", 42);
-
-            Assert.That(el.Element("foo").Value, Is.EqualTo("42"));
-        }
-
-        [Test]
-        public void BoolToElement() {
-            var el = new XElement("data");
-            el.El("foo", true);
-            el.El("bar", false);
-
-            Assert.That(el.Element("foo").Value, Is.EqualTo("true"));
-            Assert.That(el.Element("bar").Value, Is.EqualTo("false"));
-        }
-
-        [Test]
-        public void DateTimeToElement() {
-            var el = new XElement("data");
-            el.El("foo", new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc));
-
-            Assert.That(el.Element("foo").Value, Is.EqualTo("1970-05-21T13:55:21.934Z"));
-        }
-
-        [Test]
-        public void DoubleFloatDecimalToElement() {
-            var el = new XElement("data");
-            el.El("double", 12.456D);
-            el.El("float", 12.457F);
-            el.El("decimal", 12.458M);
-
-            Assert.That(el.Element("double").Value, Is.EqualTo("12.456"));
-            Assert.That(el.Element("float").Value, Is.EqualTo("12.457"));
-            Assert.That(el.Element("decimal").Value, Is.EqualTo("12.458"));
-        }
-
-        [Test]
-        public void ReadElement() {
-            var el = XElement.Parse("<data><foo>bar</foo></data>");
-
-            Assert.That(el.El("foo"), Is.EqualTo("bar"));
-            Assert.That(el.El("bar"), Is.Null);
-        }
-
-        [Test]
-        public void SerializeObject() {
-            var target = new Target {
-                AString = "foo",
-                AnInt = 42,
-                ABoolean = true,
-                ADate = new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc),
-                ADouble = 12.345D,
-                AFloat = 23.456F,
-                ADecimal = 34.567M,
-                ANullableInt = 42,
-                ANullableBoolean = true,
-                ANullableDate = new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc),
-                ANullableDouble = 12.345D,
-                ANullableFloat = 23.456F,
-                ANullableDecimal = 34.567M
-            };
-            var el = new XElement("data");
-            el.With(target)
-                .ToAttr(t => t.AString)
-                .ToAttr(t => t.AnInt)
-                .ToAttr(t => t.ABoolean)
-                .ToAttr(t => t.ADate)
-                .ToAttr(t => t.ADouble)
-                .ToAttr(t => t.AFloat)
-                .ToAttr(t => t.ADecimal)
-                .ToAttr(t => t.ANullableInt)
-                .ToAttr(t => t.ANullableBoolean)
-                .ToAttr(t => t.ANullableDate)
-                .ToAttr(t => t.ANullableDouble)
-                .ToAttr(t => t.ANullableFloat)
-                .ToAttr(t => t.ANullableDecimal);
-
-
-            Assert.That(el.Attr("AString"), Is.EqualTo("foo"));
-            Assert.That(el.Attr("AnInt"), Is.EqualTo("42"));
-            Assert.That(el.Attr("ABoolean"), Is.EqualTo("true"));
-            Assert.That(el.Attr("ADate"), Is.EqualTo("1970-05-21T13:55:21.934Z"));
-            Assert.That(el.Attr("ADouble"), Is.EqualTo("12.345"));
-            Assert.That(el.Attr("AFloat"), Is.EqualTo("23.456"));
-            Assert.That(el.Attr("ADecimal"), Is.EqualTo("34.567"));
-            Assert.That(el.Attr("ANullableInt"), Is.EqualTo("42"));
-            Assert.That(el.Attr("ANullableBoolean"), Is.EqualTo("true"));
-            Assert.That(el.Attr("ANullableDate"), Is.EqualTo("1970-05-21T13:55:21.934Z"));
-            Assert.That(el.Attr("ANullableDouble"), Is.EqualTo("12.345"));
-            Assert.That(el.Attr("ANullableFloat"), Is.EqualTo("23.456"));
-            Assert.That(el.Attr("ANullableDecimal"), Is.EqualTo("34.567"));
-        }
-
-        [Test]
-        public void DeSerializeObject() {
-            var target = new Target();
-            var el =
-                XElement.Parse(
-                    "<data AString=\"foo\" AnInt=\"42\" ABoolean=\"true\" " +
-                    "ADate=\"1970-05-21T13:55:21.934Z\" ADouble=\"12.345\" " +
-                    "AFloat=\"23.456\" ADecimal=\"34.567\" " +
-                    "ANullableInt=\"42\" ANullableBoolean=\"true\" " +
-                    "ANullableDate=\"1970-05-21T13:55:21.934Z\" ANullableDouble=\"12.345\" " +
-                    "ANullableFloat=\"23.456\" ANullableDecimal=\"34.567\"/>");
-            el.With(target)
-                .FromAttr(t => t.AString)
-                .FromAttr(t => t.AnInt)
-                .FromAttr(t => t.ABoolean)
-                .FromAttr(t => t.ADate)
-                .FromAttr(t => t.ADouble)
-                .FromAttr(t => t.AFloat)
-                .FromAttr(t => t.ADecimal)
-                .FromAttr(t => t.ANullableInt)
-                .FromAttr(t => t.ANullableBoolean)
-                .FromAttr(t => t.ANullableDate)
-                .FromAttr(t => t.ANullableDouble)
-                .FromAttr(t => t.ANullableFloat)
-                .FromAttr(t => t.ANullableDecimal);
-
-            Assert.That(target.AString, Is.EqualTo("foo"));
-            Assert.That(target.AnInt, Is.EqualTo(42));
-            Assert.That(target.ABoolean, Is.True);
-            Assert.That(target.ADate, Is.EqualTo(new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc)));
-            Assert.That(target.ADouble, Is.EqualTo(12.345D));
-            Assert.That(target.AFloat, Is.EqualTo(23.456F));
-            Assert.That(target.ADecimal, Is.EqualTo(34.567M));
-            Assert.That(target.ANullableInt, Is.EqualTo(42));
-            Assert.That(target.ANullableBoolean, Is.True);
-            Assert.That(target.ANullableDate, Is.EqualTo(new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc)));
-            Assert.That(target.ANullableDouble, Is.EqualTo(12.345D));
-            Assert.That(target.ANullableFloat, Is.EqualTo(23.456F));
-            Assert.That(target.ANullableDecimal, Is.EqualTo(34.567M));
-        }
-
-        [Test]
-        public void DeSerializeFromMissingAttributeLeavesValueIntact() {
-            var target = new Target {
-                AString = "foo",
-                AnInt = 42,
-                ABoolean = true,
-                ADate = new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc),
-                ADouble = 12.345D,
-                AFloat = 23.456F,
-                ADecimal = 34.567M,
-                ANullableInt = 42,
-                ANullableBoolean = true,
-                ANullableDate = new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc),
-                ANullableDouble = 12.345D,
-                ANullableFloat = 23.456F,
-                ANullableDecimal = 34.567M
-            };
-            var el = new XElement("data");
-            el.With(target)
-                .FromAttr(t => t.AString)
-                .FromAttr(t => t.AnInt)
-                .FromAttr(t => t.ABoolean)
-                .FromAttr(t => t.ADate)
-                .FromAttr(t => t.ADouble)
-                .FromAttr(t => t.AFloat)
-                .FromAttr(t => t.ADecimal)
-                .FromAttr(t => t.ANullableInt)
-                .FromAttr(t => t.ANullableBoolean)
-                .FromAttr(t => t.ANullableDate)
-                .FromAttr(t => t.ANullableDouble)
-                .FromAttr(t => t.ANullableFloat)
-                .FromAttr(t => t.ANullableDecimal);
-
-            Assert.That(target.AString, Is.EqualTo("foo"));
-            Assert.That(target.AnInt, Is.EqualTo(42));
-            Assert.That(target.ABoolean, Is.True);
-            Assert.That(target.ADate, Is.EqualTo(new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc)));
-            Assert.That(target.ADouble, Is.EqualTo(12.345D));
-            Assert.That(target.AFloat, Is.EqualTo(23.456F));
-            Assert.That(target.ADecimal, Is.EqualTo(34.567M));
-            Assert.That(target.ANullableInt, Is.EqualTo(42));
-            Assert.That(target.ANullableBoolean, Is.True);
-            Assert.That(target.ANullableDate, Is.EqualTo(new DateTime(1970, 5, 21, 13, 55, 21, 934, DateTimeKind.Utc)));
-            Assert.That(target.ANullableDouble, Is.EqualTo(12.345D));
-            Assert.That(target.ANullableFloat, Is.EqualTo(23.456F));
-            Assert.That(target.ANullableDecimal, Is.EqualTo(34.567M));
-        }
-
-        [Test]
-        public void AttrWithContext() {
-            var el = new XElement("data")
-                .With(new {foo = 123})
-                .ToAttr(o => o.foo);
-            var val = el.Attr(o => o.foo);
-
-            Assert.That(val, Is.EqualTo(123));
-        }
-
-        [Test]
-        public void ContextSwitch() {
-            var el = new XElement("data");
-            el.With(new {foo = "bar"})
-                .ToAttr(o => o.foo)
-                .With(new {bar = "baz"})
-                .ToAttr(o => o.bar);
-
-            Assert.That(el.Attr<string>("foo"), Is.EqualTo("bar"));
-            Assert.That(el.Attr<string>("bar"), Is.EqualTo("baz"));
-        }
-
-        [Test]
-        public void ImplicitConversion() {
-            var el = new XElement("data")
-                .With(new {foo = "bar"})
-                .ToAttr(o => o.foo);
-            Func<XElement, string> func = e => e.Attr<string>("foo");
-
-            Assert.That(func(el), Is.EqualTo("bar"));
-        }
-
-        [Test]
-        public void NullSerializes() {
-            var target = new Target();
-            var el = new XElement("data");
-            el.With(target)
-                .ToAttr(t => t.AString)
-                .ToAttr(t => t.ANullableInt)
-                .ToAttr(t => t.ANullableBoolean)
-                .ToAttr(t => t.ANullableDate)
-                .ToAttr(t => t.ANullableDouble)
-                .ToAttr(t => t.ANullableFloat)
-                .ToAttr(t => t.ANullableDecimal);
-
-            Assert.That(el.Attr("AString"), Is.EqualTo(""));
-            Assert.That(el.Attr("ANullableInt"), Is.EqualTo("null"));
-            Assert.That(el.Attr("ANullableBoolean"), Is.EqualTo("null"));
-            Assert.That(el.Attr("ANullableDate"), Is.EqualTo("null"));
-            Assert.That(el.Attr("ANullableDouble"), Is.EqualTo("null"));
-            Assert.That(el.Attr("ANullableFloat"), Is.EqualTo("null"));
-            Assert.That(el.Attr("ANullableDecimal"), Is.EqualTo("null"));
-        }
-
-        [Test]
-        public void DeSerializeNull() {
-            var target = new Target();
-            var el =
-                XElement.Parse(
-                    "<data AString=\"null\" ANullableInt=\"null\" ANullableBoolean=\"null\" " +
-                    "ANullableDate=\"null\" ANullableDouble=\"null\" " +
-                    "ANullableFloat=\"null\" ANullableDecimal=\"null\"/>");
-            el.With(target)
-                .FromAttr(t => t.AString)
-                .FromAttr(t => t.ANullableInt)
-                .FromAttr(t => t.ANullableBoolean)
-                .FromAttr(t => t.ANullableDate)
-                .FromAttr(t => t.ANullableDouble)
-                .FromAttr(t => t.ANullableFloat)
-                .FromAttr(t => t.ANullableDecimal);
-
-            Assert.That(target.AString, Is.EqualTo("null"));
-            Assert.That(target.ANullableInt, Is.Null);
-            Assert.That(target.ANullableBoolean, Is.Null);
-            Assert.That(target.ANullableDate, Is.Null);
-            Assert.That(target.ANullableDouble, Is.Null);
-            Assert.That(target.ANullableFloat, Is.Null);
-            Assert.That(target.ANullableDecimal, Is.Null);
-        }
-