I am trying to implement a custom payment type for Serialized carts, it is failing when we convert the cart to a purchase order. Below are the different ways I tried out but I am still having the same issue.
Attempt 1: Inheriting from Payment (I opened the reflector and I see payment types like CashCardPayment, CreditCardPayment etc are doing the same)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using EPiServer.Commerce.Order;
using Mediachase.Commerce.Orders;
using Mediachase.MetaDataPlus.Configurator;
namespace Whereoware.Commerce.Payments
{
[Serializable]
public class TermsPayment : Payment
{
private static MetaClass _MetaClass;
/// <summary>
/// Gets the credit card payment meta class.
/// </summary>
/// <value>The credit card payment meta class.</value>
public static MetaClass TermsPaymentMetaClass => _MetaClass ?? (_MetaClass = MetaClass.Load(OrderContext.MetaDataContext, "TermsPayment"));
public TermsPayment() : base(TermsPayment.TermsPaymentMetaClass)
{
this.PaymentType = PaymentType.Other;
this.ImplementationClass = GetType().AssemblyQualifiedName;
}
public TermsPayment(MetaClass metaClass) : base(metaClass)
{
this.PaymentType = PaymentType.Other;
this.ImplementationClass = GetType().AssemblyQualifiedName;
}
protected TermsPayment(SerializationInfo info, StreamingContext context) : base(info, context)
{
this.PaymentType = PaymentType.Other;
this.ImplementationClass = GetType().AssemblyQualifiedName;
}
public string TermsOption
{
get
{
return this.GetString("TermsOption");
}
set
{
this["TermsOption"] = (object)value;
}
}
public string PurchaseOrderNumber
{
get
{
return this.GetString("PurchaseOrderNumber");
}
set
{
this["PurchaseOrderNumber"] = (object)value;
}
}
}
}
I also have a TermsPaymentMethod class that implements PaymentMethodBase which is an abstract class.
using System;
using EPiServer.Commerce.Order;
using EPiServer.ServiceLocation;
using Mediachase.Commerce.Orders.Managers;
namespace CustomPayment.Commerce.PaymentMethods
{
[ServiceConfiguration(typeof(IPaymentMethod))]
public class TermsPaymentMethod : PaymentMethodBase
{
public override string SystemKeyword => "Manual";
protected readonly Guid _paymentMethodId;
public TermsPaymentMethod(Guid paymentMethodId) : this(ServiceLocator.Current.GetInstance<IOrderGroupFactory>(), paymentMethodId)
{
_paymentMethodId = paymentMethodId;
}
public TermsPaymentMethod(IOrderGroupFactory orderGroupFactory, Guid paymentMethodId) : base(orderGroupFactory, paymentMethodId)
{
}
public override IPayment CreatePayment(decimal amount, IOrderGroup orderGroup)
{
string implementationClassName = PaymentManager.GetPaymentMethod(base.PaymentMethodId, false).PaymentMethod[0].PaymentImplementationClassName;
var type = Type.GetType(implementationClassName);
var payment = type == null ? orderGroup.CreatePayment(OrderGroupFactory) : orderGroup.CreatePayment(OrderGroupFactory, type);
payment.PaymentMethodId = PaymentMethodId;
payment.PaymentMethodName = Name;
payment.Amount = amount;
payment.PaymentType = Mediachase.Commerce.Orders.PaymentType.Other;
return payment;
}
public override bool ValidateData()
{
return true;
}
}
}
I then create a Payment type inside my checkout controller and use the CreatePayment(decimal amount, IOrderGroup orderGroup) of IPaymentMethod which in my case triggers TermsPaymentMethod implementation.
I can see that my implementation class is now TermsPayment as well in the payment object. But it fails when I convert the cart to a purchase order.
https://www.screencast.com/t/y0czxqCnP5m
Below is the stack trace:
Activation error occurred while trying to get instance of type, key "TermsPayment"
at EPiServer.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key)
at EPiServer.Commerce.Order.Internal.OrderGroupBuilder.CreatePayment(Type paymentType)
at EPiServer.Commerce.Order.IOrderGroupExtensions.CopyPayments(IOrderForm sourceForm, IOrderForm destinationForm, IOrderGroup destinationOrderGroup, IOrderGroupFactory orderGroupFactory)
at EPiServer.Commerce.Order.IOrderGroupExtensions.CopyOrderForm(IOrderGroup orderGroup, IOrderGroup sourceOrderGroup, IOrderGroupFactory orderGroupFactory)
at EPiServer.Commerce.Order.IOrderGroupExtensions.CopyFrom(IOrderGroup orderGroup, IOrderGroup sourceOrderGroup, IOrderGroupFactory orderGroupFactory)
at EPiServer.Commerce.Order.Internal.SerializableCartProvider.SaveAsPurchaseOrder(ICart cart)
at EPiServer.Commerce.Order.Internal.DefaultOrderRepository.SaveAsPurchaseOrder(IOrderGroup cart)
Upon checking the reflector I see that Epi tries to create a payment type :
public IPayment CreatePayment(Type paymentType)
{
return ServiceLocator.Current.GetInstance(paymentType) as IPayment;
}
It is unable to convert it to IPayment and gives a null exception.
Attempt 2:
Everything is the same as the previous attempt other than the payment type implementation
using System.Collections;
using EPiServer.Commerce.Order;
using EPiServer.Commerce.Order.Internal;
using EPiServer.Commerce.Storage;
using Mediachase.Commerce.Orders;
using Newtonsoft.Json;
namespace Whereoware.Commerce.Payments
{
[JsonConverter(typeof(PaymentConverter))]
public class TermsPayment : SerializablePayment, IPayment, IExtendedProperties
{
public TermsPayment()
{
this.Properties = new Hashtable();
this.BillingAddress = (IOrderAddress)new SerializableOrderAddress();
this.PaymentType = PaymentType.Other;
this.ImplementationClass = GetType().AssemblyQualifiedName;
}
}
}
I refferd to the article below for the second attempt:
But still have the same issue when it converts it to a purchase order:
"Value cannot be null"
at Mediachase.Commerce.Storage.MetaStorageObservableCollection`2.OnListChanged(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
at EPiServer.Commerce.Order.IOrderGroupExtensions.CopyPayments(IOrderForm sourceForm, IOrderForm destinationForm, IOrderGroup destinationOrderGroup, IOrderGroupFactory orderGroupFactory)
at EPiServer.Commerce.Order.IOrderGroupExtensions.CopyOrderForm(IOrderGroup orderGroup, IOrderGroup sourceOrderGroup, IOrderGroupFactory orderGroupFactory)
at EPiServer.Commerce.Order.IOrderGroupExtensions.CopyFrom(IOrderGroup orderGroup, IOrderGroup sourceOrderGroup, IOrderGroupFactory orderGroupFactory)
at EPiServer.Commerce.Order.Internal.SerializableCartProvider.SaveAsPurchaseOrder(ICart cart)
at EPiServer.Commerce.Order.Internal.DefaultOrderRepository.SaveAsPurchaseOrder(IOrderGroup cart)
I am also attaching a screen shot of the PaymentMethod table:
https://www.screencast.com/t/tRdQ5xs4
I have been struggling on this since 2 days now, any help or leads on this is highly appreciated.