日本語版
最新ニュース
企業

Optimizely Commerce 14 でのカスタム支払いの構築 (単純な「アカウント」メソッドを使用) – PowerBuilder

Commerce 14 にはこのフローに関する公式のステップバイステップのドキュメントがないため、プラットフォームの一部を逆コンパイルし、(古い) 商業書籍 それをつなぎ合わせます。この投稿では、最小限の実用的なパスを示し、必要な部分のみを説明します。詳細は説明しません。 サンプルは Foundation に基づいています。いくつかのヘルパー クラスはそのプロジェクトから再利用されているため、ここには示されていません。不足しているヘルパーは Foundation リポジトリからコピーできます。 https://github.com/episerver/Foundation/tree/main 私たちが構築しているもの 非常に簡単な機能を追加します アカウント 支払い: あ 支払いオプション それは AccountNumber そして作成します Payment 注文に入れるオブジェクト。 あ ペイメントゲートウェイ Optimizely が処理 (承認/取得/返金) 中に呼び出すもの。 あ 支払い設定 管理 UI で、以下を選択します。 クラス名 → ゲートウェイ クラス…

Optimizely Commerce 14 でのカスタム支払いの構築 (単純な「アカウント」メソッドを使用) – PowerBuilder

1760784535
2025-10-17 19:41:00

Commerce 14 にはこのフローに関する公式のステップバイステップのドキュメントがないため、プラットフォームの一部を逆コンパイルし、(古い) 商業書籍 それをつなぎ合わせます。この投稿では、最小限の実用的なパスを示し、必要な部分のみを説明します。詳細は説明しません。

サンプルは Foundation に基づいています。いくつかのヘルパー クラスはそのプロジェクトから再利用されているため、ここには示されていません。不足しているヘルパーは Foundation リポジトリからコピーできます。 https://github.com/episerver/Foundation/tree/main

私たちが構築しているもの

非常に簡単な機能を追加します アカウント 支払い:

  1. 支払いオプション それは AccountNumber そして作成します Payment 注文に入れるオブジェクト。
  2. ペイメントゲートウェイ Optimizely が処理 (承認/取得/返金) 中に呼び出すもの。
  3. 支払い設定 管理 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

執筆者について: nipponese

Nipponese News編集部は、国内外のニュースを日本語で分かりやすくお届けします。