Для этого процесса в Commerce 14 нет официальной пошаговой документации, поэтому я декомпилировал части платформы и опирался на (более старую) версию. Книги по коммерции чтобы собрать это воедино. В этом посте показан минимальный рабочий путь и объяснено только то, что вам нужно, без глубоких погружений.
Я взял за основу образец Foundation; несколько вспомогательных классов повторно используются из этого проекта и здесь не показаны. Вы можете скопировать недостающие помощники из репозитория Foundation: https://github.com/episerver/Foundation/tree/main
Что мы строим
Мы добавим очень простой Счет оплата:
- А Вариант оплаты который собирает
AccountNumberи создаетPaymentобъект для внесения в заказ. - А Платежный шлюз который Optimizely вызывает во время обработки (авторизация/захват/возврат).
- А Конфигурация оплаты в интерфейсе администратора, где вы выбираете:
- Имя класса → ваш класс шлюза (реализует
IPaymentGateway) - Класс оплаты → бетон
Paymentподкласс, который возвращает ваш вариант (мы будем использоватьOtherPaymentчтобы было проще). «Класс платежа» явно является классом, который наследуетMediachase.Commerce.Orders.Payment.
- Имя класса → ваш класс шлюза (реализует
1) Вариант оплаты — AccountPaymentOption
Это модель пользовательского интерфейса, которая отображает «Учетную запись» при оформлении заказа, проверяет вводимые данные и создает Payment.
using YourNamespace.Web.Infrastructure.Commerce.Markets;
using Mediachase.Commerce.Orders;
using Mediachase.Commerce.Orders.Managers;
namespace YourNamespace.Web.Features.Checkout.Payments
{
public class AccountPaymentOption(
LocalizationService localizationService,
IOrderGroupFactory orderGroupFactory,
ICurrentMarket currentMarket,
LanguageService languageService,
IPaymentService paymentService)
: PaymentOptionBase(localizationService, orderGroupFactory, currentMarket, languageService, paymentService)
{
public override string SystemKeyword => "Account";
public string AccountNumber { get; set; } = string.Empty;
public override IPayment CreatePayment(decimal amount, IOrderGroup orderGroup)
{
var 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 = SystemKeyword;
payment.Amount = amount;
payment.PaymentType = PaymentType.Other;
// Custom data for the gateway
payment.Properties["AccountNumber"] = this.AccountNumber;
return payment;
}
public override bool ValidateData()
{
// minimal validation; expand as needed
return !string.IsNullOrWhiteSpace(AccountNumber);
}
}
}
Почему существует этот класс
Checkout нужен компонент для собирать данные и вернуть Payment возражать против OrderForm.Payments.
2) Платежный шлюз — AccountPaymentGateway
Это последняя точка интеграции между Optimizely Commerce и вашим платежным провайдером/логикой. Каждый новый тип платежа должен иметь шлюз.
using Mediachase.Commerce.Orders;
using Mediachase.Commerce.Plugins.Payment;
namespace YourNamespace.Web.Features.Checkout.Payments
{
public class AccountPaymentGateway : AbstractPaymentGateway, IPaymentPlugin
{
public PaymentProcessingResult ProcessPayment(IOrderGroup orderGroup, IPayment payment)
{
if (string.IsNullOrEmpty(payment.Properties["AccountNumber"]?.ToString()))
{
return PaymentProcessingResult.CreateUnsuccessfulResult("Invalid account number.");
}
// Your custom business logic / external call would go here
return PaymentProcessingResult.CreateSuccessfulResult("");
}
public override bool ProcessPayment(Payment payment, ref string message)
{
var result = ProcessPayment(null, payment);
message = result.Message;
return result.IsSuccessful;
}
}
}
Почему существует этот класс
Шлюзы реализуют обработка сторона (авторизация/захват/возврат).
3) Подключение (DI) и настройка администратора
Зарегистрируйте способ оплаты, чтобы ваша касса могла решить эту проблему:
// in an initialization module
context.Services.AddTransient();
Затем в Настройки → Платежи добавить новый платеж:
- Имя класса → ваш полностью квалифицированный
AccountPaymentGateway - Класс оплаты →
Mediachase.Commerce.Orders.OtherPayment - Рынок → Рынок по умолчанию
Почему эти два поля важны:
- Имя класса должен указывать на ваш шлюз (реализация
IPaymentGateway). - Класс оплаты должен быть конкретным подклассом
Paymentчто ваш вариант возвращается.
4) (Необязательно, дополнительно) Сохранить AccountNumber в своей таблице
⚠️ Не рекомендуется, если вам действительно не нужен такой первоклассный стол, как OrderFormPayment_CreditCard. В Commerce 14 удален старый Commerce Manager, который использовался для их создания; теперь ты должен сделать это сам. Продолжайте, только если вы понимаете риски, связанные с обновлениями и обслуживанием.
4.1 Создание бетона Payment подкласс
using System.Runtime.Serialization;
using Mediachase.Commerce.Orders;
using Mediachase.MetaDataPlus.Configurator;
namespace YourNamespace.Web.Features.Checkout.Payments
{
[Serializable]
public class AccountPayment : Payment
{
public string AccountNumber
{
get => this.GetString(nameof(AccountNumber));
set => this[nameof(AccountNumber)] = value;
}
private static MetaClass? _metaClass;
public static MetaClass GenericAccountMetaClass
{
get
{
_metaClass ??= MetaClass.Load(OrderContext.MetaDataContext, nameof(AccountPayment));
return _metaClass;
}
}
public AccountPayment() : base(GenericAccountMetaClass, GetDefaultValue) { }
private static object? GetDefaultValue(string fieldName)
=> "PaymentType".Equals(fieldName) ? PaymentType.Other : null;
public AccountPayment(SerializationInfo info, StreamingContext context) : base(info, context)
{
this.PaymentType = PaymentType.Other;
this.ImplementationClass = GetType().AssemblyQualifiedName;
}
}
}
Если вы используете этот класс, смените администратора Класс оплаты к YourNamespace.Web.Features.Checkout.Payments.AccountPayment

4.2 Создайте резервные таблицы
Запустите это точный сценарий в вашей базе данных Commerce для создания таблиц и FK:
CREATE TABLE [dbo].[OrderFormPayment_Account](
[ObjectId] [int] NOT NULL,
[CreatorId] [nvarchar](100) NULL,
[Created] [datetime] NULL,
[ModifierId] [nvarchar](100) NULL,
[Modified] [datetime] NULL,
[AccountNumber] [nvarchar](512) NULL,
CONSTRAINT [PK_OrderFormPayment_Account] PRIMARY KEY CLUSTERED
(
[ObjectId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[OrderFormPayment_Account] WITH CHECK ADD CONSTRAINT [FK_OrderFormPayment_Account_OrderFormPayment] FOREIGN KEY([ObjectId])
REFERENCES [dbo].[OrderFormPayment] ([PaymentId])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[OrderFormPayment_Account] CHECK CONSTRAINT [FK_OrderFormPayment_Account_OrderFormPayment]
GO
CREATE TABLE [dbo].[OrderFormPayment_Account_Localization](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ObjectId] [int] NOT NULL,
[ModifierId] [nvarchar](100) NULL,
[Modified] [datetime] NULL,
[Language] [nvarchar](20) NOT NULL,
[AccountNumber] [nvarchar](512) NULL,
CONSTRAINT [PK_OrderFormPayment_Account_Localization] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[OrderFormPayment_Account_Localization] WITH CHECK ADD CONSTRAINT [FK_OrderFormPayment_Account_Localization_OrderFormPayment] FOREIGN KEY([ObjectId])
REFERENCES [dbo].[OrderFormPayment] ([PaymentId])
ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[OrderFormPayment_Account_Localization] CHECK CONSTRAINT [FK_OrderFormPayment_Account_Localization_OrderFormPayment]
GO
4.3 Регистрация метакласса и отношений полей
Этот поднимает настроение а MetaClass названный AccountPaymentдобавляет MetaField называется AccountNumberотражает стандартные поля, используемые CashCard, и связывает ваше новое поле:
BEGIN TRY
BEGIN TRAN;
----------------------------------------------------------------------
-- 1) Upsert MetaClass: AccountPayment
----------------------------------------------------------------------
DECLARE @NewMetaClassId int;
SELECT @NewMetaClassId = mc.MetaClassId
FROM MetaClass mc
WHERE mc.[Namespace] = N'Mediachase.Commerce.Orders.System'
AND mc.[Name] = N'AccountPayment';
IF @NewMetaClassId IS NULL
BEGIN
DECLARE @tNewClass table (MetaClassId int);
INSERT INTO MetaClass
(Namespace, Name, FriendlyName, IsSystem, IsAbstract, ParentClassId, TableName, PrimaryKeyName, [Description])
OUTPUT INSERTED.MetaClassId INTO @tNewClass(MetaClassId)
SELECT TOP (1)
N'Mediachase.Commerce.Orders.System',
N'AccountPayment',
N'Account Payment',
0,
0,
10,
N'OrderFormPayment_Account',
N'PK_OrderFormPayment_Account',
N'Account payment'
FROM MetaClass
WHERE Name LIKE N'CashCard%';
SELECT @NewMetaClassId = MetaClassId FROM @tNewClass;
END;
IF @NewMetaClassId IS NULL
BEGIN
INSERT INTO MetaClass
(Namespace, Name, FriendlyName, IsSystem, IsAbstract, ParentClassId, TableName, PrimaryKeyName, [Description])
VALUES
(N'Mediachase.Commerce.Orders.System',
N'AccountPayment',
N'Account Payment',
0, 0, 10,
N'OrderFormPayment_Account',
N'PK_OrderFormPayment_Account',
N'Account payment');
SET @NewMetaClassId = SCOPE_IDENTITY();
END;
----------------------------------------------------------------------
-- 2) Upsert MetaField: AccountNumber
----------------------------------------------------------------------
DECLARE @NewMetaFieldId int;
SELECT @NewMetaFieldId = mf.MetaFieldId
FROM MetaField mf
WHERE mf.[Name] = N'AccountNumber'
AND mf.[Namespace] = N'Mediachase.Commerce.Orders.System';
IF @NewMetaFieldId IS NULL
BEGIN
INSERT INTO MetaField
(Name, Namespace, SystemMetaClassId, FriendlyName, [Description],
DataTypeId, [Length], AllowNulls, MultiLanguageValue, AllowSearch, IsEncrypted, IsKeyField)
VALUES
(N'AccountNumber', N'Mediachase.Commerce.Orders.System', 0,
N'Cash Card Number', N'Contains cash card number',
31, 512, 1, 0, 0, 0, 0);
SET @NewMetaFieldId = SCOPE_IDENTITY();
END;
----------------------------------------------------------------------
-- 3) Relations: MetaClassMetaFieldRelation
----------------------------------------------------------------------
-- 3a) Mirror fields related to any *CashCard* class (and SystemMetaClassId = 10) onto the new class
INSERT INTO MetaClassMetaFieldRelation (MetaClassId, MetaFieldId, [Weight], Enabled)
SELECT DISTINCT
@NewMetaClassId,
f.MetaFieldId,
0,
1
FROM MetaField f
INNER JOIN MetaClassMetaFieldRelation r ON f.MetaFieldId = r.MetaFieldId
INNER JOIN MetaClass c ON c.MetaClassId = r.MetaClassId
WHERE c.[Name] LIKE N'%CashCard%'
AND f.SystemMetaClassId = 10
AND NOT EXISTS (
SELECT 1
FROM MetaClassMetaFieldRelation r2
WHERE r2.MetaClassId = @NewMetaClassId
AND r2.MetaFieldId = f.MetaFieldId
);
-- 3b) Ensure the new "AccountNumber" field is related to the new class
IF NOT EXISTS (
SELECT 1
FROM MetaClassMetaFieldRelation
WHERE MetaClassId = @NewMetaClassId
AND MetaFieldId = @NewMetaFieldId
)
BEGIN
INSERT INTO MetaClassMetaFieldRelation (MetaClassId, MetaFieldId, [Weight], Enabled)
VALUES (@NewMetaClassId, @NewMetaFieldId, 0, 1);
END;
-- 3c) Run the metadata proc generator for your new class
EXEC mdpsp_sys_CreateMetaClassProcedure @NewMetaClassId;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 ROLLBACK TRAN;
DECLARE @Err nvarchar(2048) = ERROR_MESSAGE();
RAISERROR('Provisioning AccountPayment failed: %s', 16, 1, @Err);
END CATCH;
GO
4.4 Удаление крючка в ecf_OrderForm_Delete
Этот шаг изменяет стандартную процедуру для удаления ваших пользовательских строк. Не рекомендуется, но если вы зеркалируете устаревший шаблон, вам необходимо добавить вызов процедуры удаления:
-- inside the "Delete payments" cursor loop:
EXEC [dbo].[mdpsp_avto_OrderFormPayment_Account_Delete] @TempObjectId;
-- (the built-in ones are already there:)
EXEC [dbo].[mdpsp_avto_OrderFormPayment_CashCard_Delete] @TempObjectId;
EXEC [dbo].[mdpsp_avto_OrderFormPayment_CreditCard_Delete] @TempObjectId;
EXEC [dbo].[mdpsp_avto_OrderFormPayment_GiftCard_Delete] @TempObjectId;
EXEC [dbo].[mdpsp_avto_OrderFormPayment_Invoice_Delete] @TempObjectId;
EXEC [dbo].[mdpsp_avto_OrderFormPayment_Other_Delete] @TempObjectId;
EXEC [dbo].[mdpsp_avto_OrderFormPayment_Exchange_Delete] @TempObjectId;
Читайте также

