لماذا قمنا بترحيل صفحة الدفع في Shopify من اختراقات Liquid إلى Checkout Extensibility

قنبلة صفحة الدفع الموقوتة
لمدة ثلاث سنوات، كانت صفحة دفع Shopify Plus لدينا تعمل على مزيج هشّ من overrides لـ checkout.liquid، وبرامج النصية التابعة لأطراف ثالثة، وبرامج Shopify Scripts النصية التي لم يرغب أحد في امتلاكها. كانت صفحة الدفع تبدو مخصصة، لكن التنفيذ كان عبئًا. كل تحديث لـ Shopify يعني مراجعة diff تستغرق نصف يوم. كل وسيلة دفع جديدة تتطلب إعادة اختبار القمع بالكامل. وعندما أعلنت Shopify عن إيقاف تخصيصات checkout.liquid لصفحات المعلومات والشحن والدفع، كان لدينا موعد نهائي صارم لا يمكن التفاوض حوله.
كان لدينا خياران: مضاعفة الجهد على النهج القديم والتصادم مع المنصة، أو معالجة Checkout Extensibility كمشكل معماري من الدرجة الأولى. اخترنا الخيار الثاني. استغرق الترحيل ستة أسابيع، وأزال 2,400 سطر من Liquid وJavaScript، ومنحنا صفحة دفع أسرع وأكثر قابلية للاختبار والتوسيع.
ما كلفنا إياه checkout.liquid
صفحة الدفع هي الصفحة ذات الأثر الأكبر في أي متجر إلكتروني. يمكن أن يؤدي تأخير ثانية واحدة إلى خفض معدل التحويل بعدة نقاط مئوية. كان ملف checkout.liquid لدينا قد نما ليصبح أحاديًا يتحكم في التخطيط، والتتبع، وفحوصات الاحتيال، والمبيعات الإضافية، والتحقق من الحقول المخصصة. المشكلة لم تكن في حجمه، بل في أنه أصبح لا يمكن السيطرة عليه.
لم يكن هناك فصل نظيف بين العرض ومنطق الأعمال. كانت اختبارات A/B تُنفذ عن طريق عرض علامات script بشكل مشروط بناءً على سمات سلة التسوق. كانت شارات الثقة تُحقن عبر معالجة DOM، مما كان يتعطل كلما غيّرت Shopify أسماء الفئات. ولأن الملف كان مشتركًا بين الأسواق، فإن تغييرًا لمنطقة واحدة كان يعرّض الجميع للتراجعات.
الأسوأ من ذلك، أن checkout.liquid يعمل بشكل متزامن في كل خطوة من خطوات الدفع. إضافة برنامج نصي يعيق العرض تعني أن كل متسوق يدفع الثمن، وليس فقط أولئك الذين يحتاجون إلى الميزة. لقد قمنا بتحسين الواجهة الأمامية في أماكن أخرى، لكن صفحة الدفع كانت بقعة عمياء بالنسبة لنا.
ما تغيره Checkout Extensibility
تحلّ Checkout Extensibility من Shopify النموذج الأحادي وتستبدله بنموذج تركيبي. بدلًا من ملف واحد يمتلك كل شيء، تبني صفحة الدفع من ثلاثة عناصر أساسية:
- Checkout UI Extensions لعرض مكونات React مخصصة داخل checkout Shopify.
- Web Pixels و Customer Events للتتبع والتحليلات.
- Functions للتحقق من جانب الخادم ومنطق التسعير الذي يعمل داخل بنية Shopify.
البنية معتمدة على الأحداث ومحددة النطاق. يتم تحميل UI extension فقط على الخطوة التي تحتاجها. وتعمل Function فقط عند استيفاء شرطها المحدد. وكل شيء مُدار إصدارًا وآمنًا من حيث الأنواع وقابل للنشر عبر بنية Shopify.
استراتيجية الترحيل
لم نحاول إعادة الكتابة دفعة واحدة. مخاطر كسر صفحة دفع متجر حي مرتفعة للغاية. بدلًا من ذلك، شغّلنا الأنظمة القديمة والجديدة بشكل متوازٍ، ونقلنا موضوعًا واحدًا في كل مرة.
الخطوة 1: جرد السطح القديم
قمنا بتدقيق كل سطر في checkout.liquid وتصنيفه في إحدى الفئات الأربع:
- تغييرات واجهة المستخدم التي يمكن أن تصبح Checkout UI Extensions.
- التتبع والتحليلات التي يجب نقلها إلى Web Pixels.
- قواعد التسعير والتحقق التي تنتمي إلى Shopify Functions.
- الكود الميت الذي يمكن حذفه ببساطة.
حوالي ثلاثون بالمائة من الملف وقعت في الفئة الأخيرة. وحده هذا الأمر جعل التدقيق يستحق الجهد.
الخطوة 2: بناء هيكل الامتدادات
أنشأنا تطبيق Shopify واحدًا يمتلك جميع امتدادات checkout للمتجر. عاشت كل extension في دليلها الخاص مع اختباراتها الخاصة. عاش الكود المشترك، مثل عملاء API ومساعدي التحقق، في حزمة workspace بحيث لا تستطيع الامتدادات الاعتماد على بعضها البعض عن غير قصد.
// extensions/checkout-upsell/src/CheckoutUpsell.tsx
import { useExtensionApi, TextBlock, Button } from '@shopify/checkout-ui-extensions-react';
export default function CheckoutUpsell() {
const { extension, lines, applyMetafieldsChange } = useExtensionApi();
const { appMetafields } = extension;
async function addRecommendedVariant(variantId: string) {
await applyMetafieldsChange({
type: 'updateMetafield',
namespace: 'checkout_upsell',
key: 'selected_variant',
value: variantId,
valueType: 'string',
});
}
return (
<TextBlock>Complete your kit with a matching item</TextBlock>
// Render dynamic recommendation based on cart lines
);
}الخطوة 3: نقل منطق الأعمال إلى Functions
كان التحقق من جانب الخادم هو الجزء الأكثر رعبًا في الترحيل لأن خطأً واحدًا قد يكسر التسعير بصمت. بدأنا بقاعدة خصم بسيطة مبنية على metafields وأضفنا المراقبة قبل التوسع.
// extensions/cart-validation/src/run.rs
use shopify_function::prelude::*;
use serde::Serialize;
#[derive(Serialize)]
struct Output {
discount_application_strategy: String,
}
#[shopify_function_target
query = "src/run.graphql"]
fn run(input: ResponseData) -> Result<Output> {
let eligible = input.cart.lines.iter().all(|line| {
line.merchandise.as_ref().map_or(false, |m| {
matches!(m.product.is_gift_card, Some(false))
})
});
Ok(Output {
discount_application_strategy: if eligible { "FIRST".to_string() } else { "ALL".to_string() },
})
}الخطوة 4: التحقق المتوازي والتبديل
لمدة أسبوعين، سجلنا مخرجات Functions وUI Extensions الجديدة بجانب السلوك القديم. أي اختلاف كان يطلق تنبيهًا. بمجرد أن بقي معدل الاختلاف عند الصفر لمدة ثلاثة وسبعين ساعة، عطلنا الكود القديم للميزات المنقولة.
ما تعطل وكيف أصلحناه
أول ما تعطل كان إطار اختبار A/B الخاص بنا. كان يعتمد على حقن برامج نصية مختلفة بناءً على سمات checkout. بدون checkout.liquid، اضطررنا إلى إعادة بناء تعيين التجربة كـ Web Pixel يُطلق عند بدء checkout ويدفع المتغير إلى Customer Events. كان ذلك مزيدًا من العمل مسبقًا، لكن جودة البيانات تحسنت لأن الأحداث لم تعد تُفقد عندما تفشل البرامج النصية في التحميل.
المفاجأة الثانية كانت الأداء. كنا نتوقع checkout أسرع، لكننا Underestimated مدى السرعة. إزالة برامج النصية التابعة لأطراف ثالثة التي تعيق المسار الحرج قلّصت Largest Contentful Paint لدينا على خطوة الدفع بنحو أربعين بالمائة. يتم تحميل extensions React بشكل كسول ومعزول، لذا لا يمكنها عرض core checkout الخاص بـ Shopify.
المشكلة الثالثة كانت تنظيمية. أجبرتنا Checkout Extensibility على الفصل النظيف بين منطق الواجهة الأمامية والخلفية. لم يعد بإمكان المصممين إلقاء علامة script في قالب واعتبار الأمر منتهيًا. اضطروا للتفكير بدلالة extensions محدودة النطاق وتتبع قائم على الأحداث. كانت هذه الاحتكاك مقصودة، وحسّنت جودة ميزات checkout الجديدة.
Takeaways
معاملة checkout كامتداد للمنصة بدلًا من تجاوز للقالب يغير طريقة تصميم التغييرات واختبارها ونشرها. الترحيل ليس مجرد إعادة بناء؛ إنه تحرك معماري.
ابدأ بالجرد وصنّف كل تخصيص قديم. شغّل الأنظمة القديمة والجديدة بشكل متوازٍ مع حركة مرور حقيقية قبل التبديل. انقل منطق الأعمال إلى Functions، ومنطق العرض إلى UI Extensions، والتتبع إلى Web Pixels. توقع أن بعض أدوات A/B والتحليلات ستحتاج إلى إعادة البناء. المكاسب هي صفحة دفع أسرع وأكثر أمانًا ومواءمة مع خارطة طريق Shopify بدلًا من الصدام معها.

كتبه
Miracle Kalu
Senior Full Stack Engineer
نُشر 13 يونيو 2026 · 4 دقيقة قراءة
مواصلة القراءة
