CMS & Content Platforms

Tích hợp SAP ERP với Optimizely Commerce: Hướng dẫn B2B

By Ginbok9 min read

Giới Thiệu

Trong thế giới thương mại điện tử B2B, việc tích hợp giữa hệ thống quản lý nội dung (CMS) và hệ thống quản lý doanh nghiệp (ERP) là một bài toán phức tạp nhưng vô cùng quan trọng. Bài viết này chia sẻ kinh nghiệm thực tế từ việc tích hợp SAP ERP vào nền tảng Optimizely Commerce (trước đây là Episerver) trong dự án thương mại điện tử cho ngành thiết bị nha khoa — nơi mà tính chính xác về giá, thuế, và quản lý đơn hàng là yếu tố sống còn.

Khi làm việc với các khách hàng B2B quy mô lớn, chúng ta thường gặp phải các hệ thống cũ (legacy) dựa trên các giao thức đã được thiết lập lâu đời. Hướng dẫn này khám phá cách thu hẹp khoảng cách giữa kiến trúc .NET hiện đại và cấu trúc backend mạnh mẽ nhưng cứng nhắc của SAP, đảm bảo trải nghiệm mượt mà cho cả người dùng cuối và nhân viên quản trị.

1. Tổng Quan Kiến Trúc

1.1 Bức Tranh Toàn Cảnh

Kiến trúc tích hợp được thiết kế theo mô hình layered architecture (kiến trúc phân lớp). Điều này đảm bảo sự tách biệt rõ ràng giữa các thành phần, giúp hệ thống dễ bảo trì, kiểm thử và mở rộng. Luồng dữ liệu di chuyển từ Frontend Episerver qua một lớp SAP Service chuyên dụng trước khi giao tiếp với SAP ERP qua SOAP/HTTPS.

Các tầng chính bao gồm:

1.2 Các Thành Phần Chính

Để duy trì một hệ thống mạnh mẽ, chúng tôi đã phân loại các thành phần theo các vai trò cụ thể như bảng dưới đây:

Thành Phần Vai Trò File/Namespace Chính
SAP Module Giao tiếp trực tiếp với SAP qua giao thức SOAP. SAP/OrderService.cs, SAP/CustomerService.cs
Checkout Services Lớp trung gian chuyển đổi đối tượng Commerce sang DTO cho SAP. Features/Checkout/Services/SAPOrderService.cs
Event Handlers Xử lý các sự kiện sau khi đặt hàng theo mô hình CQRS. Features/Checkout/Events/SendOrderToSAP.cs
Domain Models Cấu trúc dữ liệu dùng chung cho đơn hàng, địa chỉ và sản phẩm. SAP/Domain/SAPLineItem.cs, SAPAddress.cs
Error Handling Quản lý mã trả về từ SAP và thông báo cho admin. SAPError.cs, SendEmailToSAPAdmins.cs

2. Giao Tiếp với SAP qua SOAP Web Services

2.1 Tại Sao Lại Là SOAP?

Các hệ thống SAP ERP truyền thống (đặc biệt là ECC) sử dụng BAPI (Business Application Programming Interface). Các BAPI này thường được expose dưới dạng SOAP Web Services thông qua nền tảng SAP NetWeaver. Mặc dù REST là tiêu chuẩn hiện đại, SOAP vẫn là con đường tích hợp phổ biến và ổn định nhất cho môi trường SAP ECC nhờ vào cách tiếp cận strict contract-first (WSDL).

Trong dự án nha khoa này, chúng tôi đã sử dụng hai endpoint SOAP chính:

  1. SAPOrderService: Xử lý tất cả các thao tác liên quan đến đơn hàng bao gồm Mô phỏng (giá/thuế), Tạo đơn hàng và Lịch sử đơn hàng.
  2. SAPCustomerService: Quản lý dữ liệu khách hàng, bao gồm tạo mới, đồng bộ địa chỉ và kiểm tra hạn mức tín dụng.

2.2 Cấu Hình Kết Nối

Kết nối đến SAP được thiết lập thông qua BasicHttpBinding. Do tính chất nhạy cảm của các giao dịch B2B, chúng tôi đã sử dụng bảo mật Transport (HTTPS) kết hợp với Basic Authentication.

private serviceSoapClient GetSapOrderSoapClient()
{
    var binding = new BasicHttpBinding
    {
        Security =
        {
            Mode = BasicHttpSecurityMode.Transport,
            Transport = { ClientCredentialType = HttpClientCredentialType.Basic }
        },
        MaxReceivedMessageSize = 20000000, // 20MB cho các response lớn
        MaxBufferSize = 20000000,
        SendTimeout = TimeSpan.FromSeconds(90),
        Name = "serviceSoap"
    };

    var address = new EndpointAddress(_sapOrderEndpoint);
    var orderClient = new serviceSoapClient(binding, address);

    var credentials = orderClient.ClientCredentials;
    credentials.UserName.UserName = _sapUsername;
    credentials.UserName.Password = _sapPassword;

    return orderClient;
}

Lưu Ý Quan Trọng Khi Cấu Hình:

3. Quản Lý Đơn Hàng Real-Time

3.1 Order Simulation: Tính Giá Chính Xác

Trong B2B, giá niêm yết trên CMS thường chỉ là "giá sàn". Giá thực tế khách hàng phải trả phụ thuộc vào hợp đồng cụ thể, chiết khấu theo số lượng và thuế khu vực được lưu trong SAP. Order Simulation cho phép frontend gửi giỏ hàng hiện tại sang SAP để nhận về giá "Cuối cùng" mà không tạo ra một bản ghi vĩnh viễn.

Khi khách hàng đến bước tóm tắt thanh toán, hệ thống kích hoạt SimulateOrder(address, lineItems). Điều này đảm bảo shipmentTotaltaxTotal chính xác 100% theo logic nội bộ của SAP.

// Chuyển đổi Line Item từ Episerver sang định dạng SAP
private List<SAPLineItem> ConvertToSAPLineItems(IEnumerable<ILineItem> lineItems)
{
    var sapLineItems = new List<SAPLineItem>();
    var index = 10; // Quy ước SAP: items bắt đầu từ 10, tăng dần 10

    foreach (var lineItem in lineItems)
    {
        var variationCode = lineItem.Code;
        var sapCode = variationCode.StartsWith("_") ? variationCode.Substring(1) : variationCode;

        sapLineItems.Add(new SAPLineItem
        {
            ItemNum = index.ToString(),
            VariantCode = sapCode,
            OrderedQty = lineItem.Quantity,
            WebPrice = lineItem.PlacedPrice,
            IsProduct = !lineItem.Properties[Constants.CommerceFields.IsService].ToBool()
        });
        index += 10;
    }
    return sapLineItems;
}

3.2 Tạo Đơn Hàng và Khả Năng Chịu Lỗi

Khi khách hàng nhấn "Đặt hàng", hệ thống chuyển từ mô phỏng sang tạo thực tế. Chúng tôi sử dụng thư viện MediatR để xử lý việc này như một sự kiện tách biệt. Nếu SAP tạm thời không khả dụng, hệ thống không được phép bị sập; nó phải xử lý lỗi một cách mềm dẻo.

public async Task Handle(OrderPlaced notification, CancellationToken cancellationToken)
{
    var sapConfiguration = _settingsProvider.GetSapConfiguration();
    
    if (!sapConfiguration.SapRealTimeOrder) 
    {
        await _mediator.Publish(new StaffOrderNotification(notification.PurchaseOrder));
        return;
    }

    var errors = _sapOrderService.CreateOrder(..., out sapOrderNumber);

    if (errors.Any())
    {
        // Đánh dấu đơn hàng chờ xử lý thủ công và báo cho admin
        notification.PurchaseOrder.Status = OrderStatus.AwaitingExchange.ToString();
        await _emailToSapAdmins.SendEmailToAdmins(errors, ...);
    }
    else
    {
        // Thành công: Lưu mã tham chiếu SAP
        notification.PurchaseOrder[Constants.CommerceFields.SAPOrderNumber] = sapOrderNumber;
        await _mediator.Send(new SaveOrder(notification.PurchaseOrder));
    }
}

4. Đồng Bộ Hóa Khách Hàng Hai Chiều

Dữ liệu khách hàng trong môi trường B2B thường bắt nguồn từ SAP ("Nguồn Sự Thật"). Tuy nhiên, người dùng lại cập nhật hồ sơ của họ trên Optimizely. Giữ cho chúng đồng bộ là một thách thức lớn.

4.1 Khớp Địa Chỉ Thông Minh

Khi đồng bộ địa chỉ từ SAP ngược về Episerver, chúng tôi sử dụng logic khớp hai bước để ngăn chặn việc tạo trùng địa chỉ:

  1. Khớp Chính Xác: Dựa trên SAPAddressNumber (ID duy nhất từ SAP).
  2. Khớp Fuzzy: Nếu thiếu số ID SAP, chúng tôi khớp theo tổ hợp Thành phố + Mã bưu điện + Địa chỉ đường.

4.2 Quản Lý Danh Tính với Azure AD B2C

Khi một khách hàng mới đăng ký, chúng tôi không chỉ tạo bản ghi trong Episerver và SAP. Chúng tôi còn cập nhật Azure AD B2C. Việc lưu trữ SAPCustomerNumber dưới dạng custom attribute trong B2C cho phép xác thực liền mạch và đảm bảo danh tính người dùng nhất quán trên tất cả các ứng dụng doanh nghiệp.

5. Thông Tin Chiến Lược & Xử Lý Sự Cố

5.1 Xử Lý Các Thông Báo Trả Về Từ SAP

SAP không sử dụng mã trạng thái HTTP cho các lỗi logic nghiệp vụ. Thay vào đó, nó trả về một trường TYPE trong response:

5.2 Xử Lý Lỗi Timeout SOAP

Nếu bạn gặp phải TaskCanceledException hoặc TimeoutException thường xuyên, hãy cân nhắc:

6. Kết Luận

Tích hợp SAP ERP với Optimizely Commerce là một hành trình đòi hỏi sự hiểu biết sâu sắc về cả thực tiễn web hiện đại và logic doanh nghiệp kế thừa. Bằng cách triển khai kiến trúc phân lớp, sử dụng MediatR để xử lý theo hướng sự kiện và quản lý đồng bộ dữ liệu với logic khớp mạnh mẽ, bạn có thể xây dựng một hệ thống vừa bền bỉ vừa có khả năng mở rộng.

Bài học then chốt từ dự án thiết bị nha khoa của chúng tôi là: Đừng bao giờ tin tưởng một phản hồi mà không có kiểm chứng. Dù là tính phí vận chuyển hay số tiền thuế, hãy luôn triển khai các bước kiểm tra an toàn để đảm bảo dữ liệu chảy vào CMS của bạn là hợp lý và nhất quán.

#backend#cms13#integration#optimizely#sap
← Back to Articles