1760784535
2025-10-17 19:41:00
Commerce 14 にはこのフローに関する公式のステップバイステップのドキュメントがないため、プラットフォームの一部を逆コンパイルし、(古い) 商業書籍 それをつなぎ合わせます。この投稿では、最小限の実用的なパスを示し、必要な部分のみを説明します。詳細は説明しません。
サンプルは Foundation に基づいています。いくつかのヘルパー クラスはそのプロジェクトから再利用されているため、ここには示されていません。不足しているヘルパーは Foundation リポジトリからコピーできます。 https://github.com/episerver/Foundation/tree/main
私たちが構築しているもの
非常に簡単な機能を追加します アカウント 支払い:
- あ 支払いオプション それは
AccountNumberそして作成しますPayment注文に入れるオブジェクト。 - あ ペイメントゲートウェイ Optimizely が処理 (承認/取得/返金) 中に呼び出すもの。
- あ 支払い設定 管理 UI で、以下を選択します。
- クラス名 → ゲートウェイ クラス (実装
IPaymentGateway) - 支払いクラス →コンクリート
Paymentオプションが返すサブクラスを作成します(ここでは使用します)OtherPaymentシンプルにするため)。 「Payment Class」は明示的に継承するクラスです。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);
}
}
}
このクラスが存在する理由
チェックアウトにはコンポーネントが必要です 入力を収集する そして、を返します 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 - 市場 → デフォルトマーケット
これら 2 つのフィールドが重要な理由:
- クラス名 ゲートウェイを指す必要があります (実装
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 DB のスクリプトを使用してテーブルと 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
このステップでは、カスタム行を削除するようにストック proc を変更します。推奨されませんが、従来のパターンをミラーリングする場合は、delete proc 呼び出しを追加する必要があります。
-- 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;
#Optimizely #Commerce #でのカスタム支払いの構築 #単純なアカウントメソッドを使用 #PowerBuilder