🧱 فصل ۱۰: Blocks Cart و Checkout
از ووکامرس ۸ به بعد، فرم سبد و پرداخت کلاسیک با Blocks جایگزین شد. این تغییر بزرگ بر مبنای React و Store API است و نیاز به رویکرد جدیدی برای سفارشیسازی دارد.
🤔 چرا Blocks Cart/Checkout؟
تیم ووکامرس Cart/Checkout را با Blocks بازنویسی کرد به دلایل:
- سرعت ۲ تا ۳ برابر بیشتر – مخصوصاً در موبایل
- UX مدرن – مشابه استانداردهای ۲۰۲۴
- نرخ تبدیل بهتر – فرآیند تکصفحهای
- قابلیت توسعه با React – برای توسعهدهندهها
- سازگاری با Site Editor – تمهای Block-based
🆚 تفاوت Classic با Blocks
| ویژگی | Classic | Blocks |
|---|---|---|
| Shortcode | [woocommerce_checkout] |
<!-- wp:woocommerce/checkout --> |
| تکنولوژی | PHP + jQuery | React |
| API | WC API | Store API |
| سفارشیسازی فیلد | woocommerce_checkout_fields filter |
woocommerce_blocks_loaded + extend_schema |
| درگاه پرداخت | کلاس PHP + form fields | کلاس PHP + JS payment method |
| چندصفحهای | دارد | تکصفحهای |
| RTL پشتیبانی | کامل | کامل (نسخههای اخیر) |
🔄 آشنایی با Store API
Store API (نسخه پابلیک) دیتای cart/checkout را به React میرساند:
# دریافت محتوای سبد فعلی
GET /wp-json/wc/store/v1/cart
# افزودن به سبد
POST /wp-json/wc/store/v1/cart/add-item
{ "id": 100, "quantity": 1 }
# حذف از سبد
POST /wp-json/wc/store/v1/cart/remove-item
{ "key": "abc123..." }
# اعمال کوپن
POST /wp-json/wc/store/v1/cart/apply-coupon
{ "code": "WELCOME10" }
# Checkout (ثبت سفارش)
POST /wp-json/wc/store/v1/checkout
{
"billing_address": {...},
"shipping_address": {...},
"payment_method": "icsd_zarinpal"
}
🔧 extend_schema – افزودن فیلد سفارشی
برای اضافه کردن داده به Cart/Checkout block، از extend_schema استفاده میکنیم:
<?php
use AutomatticWooCommerceBlocksPackage;
use AutomatticWooCommerceStoreApiStoreApi;
use AutomatticWooCommerceStoreApiSchemasV1CheckoutSchema;
use AutomatticWooCommerceStoreApiSchemasV1CartSchema;
add_action('woocommerce_blocks_loaded', function() {
// ۱. اضافه کردن فیلد به Cart endpoint
woocommerce_store_api_register_endpoint_data([
'endpoint' => CartSchema::IDENTIFIER,
'namespace' => 'icsd-extras',
'data_callback' => function() {
return [
'gift_message' => WC()->session->get('gift_message', ''),
'is_gift' => (bool) WC()->session->get('is_gift', false),
];
},
'schema_callback' => function() {
return [
'gift_message' => [
'type' => 'string',
'description' => 'پیام هدیه',
],
'is_gift' => [
'type' => 'boolean',
'description' => 'آیا این سفارش هدیه است؟',
],
];
},
]);
// ۲. اضافه کردن فیلد به Checkout
woocommerce_store_api_register_endpoint_data([
'endpoint' => CheckoutSchema::IDENTIFIER,
'namespace' => 'icsd-extras',
'data_callback' => function() {
return [
'delivery_date' => '',
'national_id' => '',
];
},
'schema_callback' => function() {
return [
'delivery_date' => [
'type' => ['string', 'null'],
'description' => 'تاریخ تحویل دلخواه',
],
'national_id' => [
'type' => 'string',
'description' => 'کد ملی برای فاکتور رسمی',
],
];
},
'schema_type' => ARRAY_A,
]);
// ۳. هنگام ثبت سفارش، فیلدها را ذخیره کن
add_action('woocommerce_store_api_checkout_update_order_from_request', function($order, $request) {
$extras = $request['extensions']['icsd-extras'] ?? [];
if (!empty($extras['delivery_date'])) {
$order->update_meta_data('_delivery_date', sanitize_text_field($extras['delivery_date']));
}
if (!empty($extras['national_id'])) {
$order->update_meta_data('_billing_national_id', sanitize_text_field($extras['national_id']));
}
$order->save();
}, 10, 2);
});
🎨 ساخت Block در Checkout
برای نمایش UI سفارشی در صفحه Checkout، باید یک Block JavaScript بسازیم:
ساختار افزونه
icsd-checkout-extras/
├── icsd-checkout-extras.php
├── package.json
├── webpack.config.js
├── src/
│ ├── index.js
│ ├── block.js
│ └── style.scss
└── build/ (خروجی webpack)
{
"name": "icsd-checkout-extras",
"version": "1.0.0",
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
},
"devDependencies": {
"@woocommerce/dependency-extraction-webpack-plugin": "^3.0.0",
"@wordpress/scripts": "^27.0.0"
}
}
import { __ } from '@wordpress/i18n';
import { useState, useEffect } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
const Block = ({ checkoutExtensionData }) => {
const [deliveryDate, setDeliveryDate] = useState('');
const [nationalId, setNationalId] = useState('');
const { setExtensionData } = checkoutExtensionData;
useEffect(() => {
setExtensionData('icsd-extras', 'delivery_date', deliveryDate);
setExtensionData('icsd-extras', 'national_id', nationalId);
}, [deliveryDate, nationalId, setExtensionData]);
return (
{__('اطلاعات تکمیلی', 'icsd')}
setDeliveryDate(e.target.value)}
min={new Date(Date.now() + 2*86400*1000).toISOString().split('T')[0]}
/>
setNationalId(e.target.value.replace(/D/g, ''))}
required
/>
{__('برای صدور فاکتور رسمی', 'icsd')}
);
};
export default Block;
import { registerCheckoutBlock } from '@woocommerce/blocks-checkout';
import Block from './block';
import metadata from './block.json';
import './style.scss';
const options = {
metadata: {
...metadata,
parent: ['woocommerce/checkout-fields-block'],
},
component: Block,
};
registerCheckoutBlock(options);
<?php
/**
* Plugin Name: ICSD Checkout Extras (Blocks)
*/
defined('ABSPATH') || exit;
// بارگذاری extension
require_once __DIR__ . '/includes/blocks-extension.php';
// ثبت block
add_action('init', function() {
register_block_type(__DIR__ . '/build');
});
// ثبت aset برای frontend
add_action('woocommerce_blocks_checkout_block_registration', function($integration_registry) {
require_once __DIR__ . '/includes/class-checkout-extras-integration.php';
$integration_registry->register(new ICSD_Checkout_Extras_Integration());
});
💳 یکپارچهسازی درگاه با Blocks
درگاههای پرداخت سنتی (که در فصل ۴ ساختیم) باید به Blocks معرفی شوند:
<?php
use AutomatticWooCommerceBlocksPaymentsIntegrationsAbstractPaymentMethodType;
final class ZarinPal_Blocks_Support extends AbstractPaymentMethodType {
protected $name = 'icsd_zarinpal';
public function initialize() {
$this->settings = get_option('woocommerce_icsd_zarinpal_settings', []);
}
public function is_active() {
return !empty($this->settings['enabled']) && 'yes' === $this->settings['enabled'];
}
public function get_payment_method_script_handles() {
wp_register_script(
'wc-zarinpal-blocks',
plugins_url('build/zarinpal-block.js', __DIR__),
['wc-blocks-registry', 'wp-element', 'wp-i18n'],
'1.0.0',
true
);
return ['wc-zarinpal-blocks'];
}
public function get_payment_method_data() {
return [
'title' => $this->settings['title'] ?? 'زرینپال',
'description' => $this->settings['description'] ?? '',
'supports' => ['products', 'refunds'],
'icon' => plugins_url('assets/zarinpal-icon.png', __DIR__),
];
}
}
// ثبت
add_action('woocommerce_blocks_payment_method_type_registration', function($registry) {
$registry->register(new ZarinPal_Blocks_Support());
});
import { registerPaymentMethod } from '@woocommerce/blocks-registry';
import { getSetting } from '@woocommerce/settings';
import { __ } from '@wordpress/i18n';
const settings = getSetting('icsd_zarinpal_data', {});
const Label = ({ components }) => {
const { PaymentMethodLabel } = components;
return (
);
};
const Content = () => {
return (
{settings.description}
{__('با کلیک روی دکمه پرداخت، به درگاه امن زرینپال منتقل میشوید.', 'icsd')}
);
};
registerPaymentMethod({
name: 'icsd_zarinpal',
label: ,
content: ,
edit: ,
canMakePayment: () => true,
ariaLabel: settings.title,
supports: {
features: settings.supports,
},
});
🔄 مایگریشن از Classic به Blocks
مرحله ۱: تست در محیط Staging
- یک کپی از سایت در staging بساز
- صفحه Cart را باز کن، محتوا را به Blocks تغییر بده
- صفحه Checkout همینطور
- تست کامل: خرید، کوپن، ارسال، پرداخت
مرحله ۲: تغییر صفحات
// در صفحه Cart، شورتکد را با block جایگزین کن:
// قبل:
[woocommerce_cart]
// بعد:
<!-- wp:woocommerce/cart -->
<div class="wp-block-woocommerce-cart">...</div>
<!-- /wp:woocommerce/cart -->
مرحله ۳: مایگریشن کدهای سفارشی
| Classic | Blocks معادل |
|---|---|
woocommerce_checkout_fields filter |
extend_schema + Block |
woocommerce_review_order_before_payment |
Block در slot مناسب |
wc_add_notice() |
setValidationErrors() در React |
woocommerce_after_order_notes |
Block در checkout-fields-block |
مرحله ۴: نمایش بنر یادآوری برای کاربرانی که Classic میبینند
// در صورت تشخیص Classic Cart، بنر بزن
add_action('woocommerce_before_cart', function() {
if (!has_block('woocommerce/cart')) {
echo '<div class="alert alert-info">
🆕 سبد جدید سریعتر و راحتتر است. بهزودی فعال میشود.
</div>';
}
});
📝 خلاصه فصل
- تفاوت Classic و Blocks Cart/Checkout
- Store API برای ارتباط React با backend
- extend_schema برای افزودن داده به cart/checkout
- ساخت Block در صفحه Checkout با React
- یکپارچهسازی درگاه پرداخت با AbstractPaymentMethodType
- مایگریشن از Classic به Blocks
https://github.com/woocommerce/woocommerce-blocks/tree/trunk/docs