~/icsd.ir — bash
SYSTEM_ONLINE

📋 فصل ۳: سفارش‌ها و فرآیند پرداخت

سفارش (Order) قلب تپنده هر فروشگاه است. در این فصل چرخه کامل سفارش، تسلط بر HPOS، سفارشی‌سازی Checkout، تعریف وضعیت‌های اختصاصی و مدیریت سفارش با کد را می‌آموزیم.

سفارش (Order) قلب تپنده هر فروشگاه است. در این فصل چرخه کامل سفارش، تسلط بر HPOS، سفارشی‌سازی Checkout، تعریف وضعیت‌های اختصاصی و مدیریت سفارش با کد را می‌آموزیم.

۱. چرخه حیات سفارش

هر سفارش از مرحله سبد خرید تا تحویل، چندین وضعیت را طی می‌کند:

جریان طبیعی سفارش
سبد خرید
   ↓
صفحه پرداخت (Checkout)
   ↓
ثبت سفارش → وضعیت: pending (در انتظار پرداخت)
   ↓
هدایت به درگاه پرداخت
   ↓ (پرداخت موفق)
وضعیت: processing (در حال پردازش)
   ↓ (آماده‌سازی و ارسال)
وضعیت: completed (تکمیل شده)

مسیرهای دیگر:
   - failed (ناموفق) → پرداخت رد شد
   - cancelled (لغو شده)
   - refunded (مسترد شده)
   - on-hold (در انتظار)

۲. وضعیت‌های سفارش

وضعیت Slug توضیح
در انتظار پرداخت wc-pending پرداخت شروع نشده
در حال پردازش wc-processing پرداخت موفق – آماده‌سازی
در انتظار wc-on-hold منتظر بررسی (مثلاً کارت به کارت)
تکمیل شده wc-completed تحویل شده
لغو شده wc-cancelled توسط مشتری/مدیر لغو
مسترد شده wc-refunded مبلغ بازگشت داده شد
ناموفق wc-failed پرداخت رد شد
پیش‌نویس بررسی wc-checkout-draft قبل از ثبت نهایی
💡 پیشوند wc-: در دیتابیس و کوئری‌ها، وضعیت‌ها همیشه با wc- ذخیره می‌شوند، اما در توابع و فیلترها بدون پیشوند: مثلاً $order->update_status('processing') ولی در دیتابیس wc-processing.

۳. کلاس WC_Order در عمق

متدهای پرکاربرد

working-with-orders.php
$order = wc_get_order( $order_id );

// اطلاعات کلی
$order->get_id();                      // شناسه سفارش
$order->get_order_number();            // شماره سفارش (می‌تواند با ID متفاوت باشد)
$order->get_status();                  // وضعیت بدون wc-
$order->get_currency();                // ارز
$order->get_total();                   // مجموع
$order->get_subtotal();                // جمع آیتم‌ها
$order->get_total_tax();               // مالیات
$order->get_shipping_total();          // هزینه ارسال
$order->get_total_discount();          // تخفیف
$order->get_payment_method();          // slug درگاه
$order->get_payment_method_title();    // نام درگاه
$order->get_transaction_id();          // ID تراکنش از درگاه

// مشتری
$order->get_customer_id();             // 0 اگر مهمان باشد
$order->get_user();                    // WP_User
$order->get_billing_email();
$order->get_billing_phone();
$order->get_billing_first_name();
$order->get_billing_last_name();
$order->get_billing_address_1();
$order->get_billing_city();
$order->get_billing_state();
$order->get_billing_postcode();
$order->get_billing_country();

// آدرس ارسال (ممکن است متفاوت با صورتحساب باشد)
$order->get_shipping_first_name();
$order->get_shipping_address_1();
// ...

// آیتم‌های سفارش
foreach ( $order->get_items() as $item_id => $item ) {
    $item_name     = $item->get_name();
    $product_id    = $item->get_product_id();
    $variation_id  = $item->get_variation_id();
    $quantity      = $item->get_quantity();
    $subtotal      = $item->get_subtotal();
    $total         = $item->get_total();
    $product       = $item->get_product();
    $meta_data     = $item->get_meta_data();
}

// آیتم‌های ارسال و کوپن
foreach ( $order->get_items( 'shipping' ) as $shipping_item ) { /* ... */ }
foreach ( $order->get_items( 'fee' ) as $fee_item ) { /* ... */ }
foreach ( $order->get_items( 'coupon' ) as $coupon_item ) { /* ... */ }

// به‌روزرسانی وضعیت با یادداشت
$order->update_status( 'completed', 'سفارش با موفقیت تحویل داده شد.' );

// افزودن یادداشت داخلی (فقط ادمین می‌بیند)
$order->add_order_note( 'یادداشت داخلی: انبار شماره ۲', false );

// افزودن یادداشت برای مشتری (ایمیل می‌شود)
$order->add_order_note( 'بسته شما در حال ارسال است.', 1 );

// متادیتای سفارش
$order->update_meta_data( '_my_custom_field', 'value' );
$value = $order->get_meta( '_my_custom_field' );
$order->save();

جستجو و فیلتر سفارش‌ها

query-orders.php
// روش پیشنهادی (سازگار با HPOS)
$orders = wc_get_orders( [
    'status'        => [ 'processing', 'completed' ],
    'limit'         => 50,
    'orderby'       => 'date',
    'order'         => 'DESC',
    'date_created'  => '>=' . strtotime( '-30 days' ),
    'customer_id'   => 0, // فقط مهمان‌ها
] );

foreach ( $orders as $order ) {
    echo $order->get_id() . ' - ' . $order->get_total() . PHP_EOL;
}

// فیلتر بر اساس آیتم خاص
$orders = wc_get_orders( [
    'limit' => -1,
    'meta_key' => '_payment_method',
    'meta_value' => 'zarinpal',
] );

// سفارش‌های یک مشتری خاص
$customer_orders = wc_get_orders( [
    'customer_id' => 123,
    'limit'       => -1,
] );

ساخت سفارش با کد (برای فروش حضوری)

create-order.php
function icsd_create_manual_order( $items, $customer_data ) {
    // ساخت سفارش
    $order = wc_create_order();
    
    // افزودن محصولات
    foreach ( $items as $item ) {
        $product = wc_get_product( $item['product_id'] );
        $order->add_product( $product, $item['quantity'] );
    }
    
    // آدرس صورتحساب
    $order->set_address( [
        'first_name' => $customer_data['first_name'],
        'last_name'  => $customer_data['last_name'],
        'phone'      => $customer_data['phone'],
        'email'      => $customer_data['email'],
        'address_1'  => $customer_data['address'],
        'city'       => $customer_data['city'],
        'state'      => $customer_data['state'],
        'postcode'   => $customer_data['postcode'],
        'country'    => 'IR',
    ], 'billing' );
    
    // اگر مشتری ثبت‌نام کرده
    if ( ! empty( $customer_data['user_id'] ) ) {
        $order->set_customer_id( $customer_data['user_id'] );
    }
    
    // افزودن هزینه ارسال
    $shipping_item = new WC_Order_Item_Shipping();
    $shipping_item->set_method_title( 'پست پیشتاز' );
    $shipping_item->set_method_id( 'flat_rate' );
    $shipping_item->set_total( 50000 );
    $order->add_item( $shipping_item );
    
    // محاسبه مجموع
    $order->calculate_totals();
    
    // وضعیت اولیه
    $order->set_status( 'processing', 'سفارش حضوری توسط ادمین ثبت شد.' );
    
    // متادیتای اختصاصی
    $order->update_meta_data( '_icsd_order_source', 'in_store' );
    $order->update_meta_data( '_icsd_seller', get_current_user_id() );
    
    // ذخیره
    $order_id = $order->save();
    
    return $order_id;
}

۴. ساخت وضعیت سفارش سفارشی

گاهی نیاز به وضعیت‌های جدیدی داریم. مثلاً برای فرش‌های سفارشی: «در حال طراحی»، «در حال بافت»، «آماده تحویل».

includes/class-custom-statuses.php
<?php
class ICSD_Custom_Order_Statuses {
    
    public function __construct() {
        add_action( 'init', [ $this, 'register_statuses' ] );
        add_filter( 'wc_order_statuses', [ $this, 'add_to_list' ] );
        add_filter( 'bulk_actions-edit-shop_order', [ $this, 'add_bulk_actions' ] );
    }
    
    public function register_statuses() {
        register_post_status( 'wc-designing', [
            'label'                     => 'در حال طراحی',
            'public'                    => true,
            'show_in_admin_status_list' => true,
            'show_in_admin_all_list'    => true,
            'exclude_from_search'       => false,
            'label_count'               => _n_noop(
                'در حال طراحی (%s)',
                'در حال طراحی (%s)'
            ),
        ] );
        
        register_post_status( 'wc-weaving', [
            'label'                     => 'در حال بافت',
            'public'                    => true,
            'show_in_admin_status_list' => true,
            'show_in_admin_all_list'    => true,
            'exclude_from_search'       => false,
            'label_count'               => _n_noop(
                'در حال بافت (%s)',
                'در حال بافت (%s)'
            ),
        ] );
        
        register_post_status( 'wc-ready-to-ship', [
            'label'                     => 'آماده ارسال',
            'public'                    => true,
            'show_in_admin_status_list' => true,
            'show_in_admin_all_list'    => true,
            'exclude_from_search'       => false,
            'label_count'               => _n_noop(
                'آماده ارسال (%s)',
                'آماده ارسال (%s)'
            ),
        ] );
    }
    
    public function add_to_list( $order_statuses ) {
        $new_statuses = [];
        
        foreach ( $order_statuses as $key => $status ) {
            $new_statuses[ $key ] = $status;
            
            // بعد از processing، وضعیت‌های ما را اضافه کن
            if ( 'wc-processing' === $key ) {
                $new_statuses['wc-designing']     = 'در حال طراحی';
                $new_statuses['wc-weaving']        = 'در حال بافت';
                $new_statuses['wc-ready-to-ship']  = 'آماده ارسال';
            }
        }
        
        return $new_statuses;
    }
    
    public function add_bulk_actions( $actions ) {
        $actions['mark_designing']     = 'تغییر به: در حال طراحی';
        $actions['mark_weaving']        = 'تغییر به: در حال بافت';
        $actions['mark_ready-to-ship']  = 'تغییر به: آماده ارسال';
        return $actions;
    }
}

new ICSD_Custom_Order_Statuses();

نمایش وضعیت‌ها در پنل مشتری

functions.php
// رنگ‌بندی وضعیت‌های سفارشی در پیشخوان
add_action( 'admin_head', 'icsd_custom_status_colors' );
function icsd_custom_status_colors() {
    ?>
    
    

۵. سفارشی‌سازی Checkout

تغییر ترتیب فیلدها

functions.php
add_filter( 'woocommerce_checkout_fields', 'icsd_reorder_checkout_fields' );

function icsd_reorder_checkout_fields( $fields ) {
    // تغییر اولویت
    $fields['billing']['billing_phone']['priority'] = 25;
    $fields['billing']['billing_email']['priority'] = 26;
    
    // اجباری کردن کد ملی
    $fields['billing']['billing_national_id'] = [
        'type'        => 'text',
        'label'       => 'کد ملی',
        'placeholder' => 'مثال: 0012345678',
        'required'    => true,
        'class'       => [ 'form-row-wide' ],
        'priority'    => 27,
    ];
    
    // تغییر برچسب
    $fields['billing']['billing_address_1']['label'] = 'آدرس کامل پستی';
    $fields['billing']['billing_postcode']['label'] = 'کد پستی ۱۰ رقمی';
    
    // حذف فیلدهای غیر ضروری برای ایران
    unset( $fields['billing']['billing_company'] );
    unset( $fields['billing']['billing_address_2'] );
    
    return $fields;
}

اعتبارسنجی کد ملی ایرانی

functions.php
add_action( 'woocommerce_checkout_process', 'icsd_validate_national_id' );

function icsd_validate_national_id() {
    $national_id = $_POST['billing_national_id'] ?? '';
    
    if ( empty( $national_id ) ) {
        wc_add_notice( 'لطفاً کد ملی خود را وارد کنید.', 'error' );
        return;
    }
    
    if ( ! icsd_is_valid_national_id( $national_id ) ) {
        wc_add_notice( 'کد ملی وارد شده معتبر نیست.', 'error' );
    }
}

function icsd_is_valid_national_id( $code ) {
    $code = preg_replace( '/[^0-9]/', '', $code );
    
    if ( strlen( $code ) !== 10 ) return false;
    if ( preg_match( '/(d)1{9}/', $code ) ) return false; // اعداد تکراری
    
    $sum = 0;
    for ( $i = 0; $i < 9; $i++ ) {
        $sum += $code[ $i ] * ( 10 - $i );
    }
    
    $remainder = $sum % 11;
    $check_digit = (int) $code[9];
    
    if ( $remainder < 2 ) {
        return $check_digit === $remainder;
    }
    return $check_digit === ( 11 - $remainder );
}

// ذخیره در سفارش
add_action( 'woocommerce_checkout_update_order_meta', 'icsd_save_national_id' );
function icsd_save_national_id( $order_id ) {
    if ( ! empty( $_POST['billing_national_id'] ) ) {
        update_post_meta( $order_id, '_billing_national_id', sanitize_text_field( $_POST['billing_national_id'] ) );
    }
}

// نمایش در پیشخوان سفارش
add_action( 'woocommerce_admin_order_data_after_billing_address', 'icsd_show_national_id' );
function icsd_show_national_id( $order ) {
    $national_id = get_post_meta( $order->get_id(), '_billing_national_id', true );
    if ( $national_id ) {
        echo '

کد ملی: ' . esc_html( $national_id ) . '

'; } }

۶. فیلدهای سفارشی Checkout

افزودن فیلد در بخش‌های مختلف

functions.php
// روش ۱: افزودن قبل از فرم پرداخت
add_action( 'woocommerce_review_order_before_payment', 'icsd_add_gift_field' );
function icsd_add_gift_field() {
    woocommerce_form_field( 'icsd_is_gift', [
        'type'    => 'checkbox',
        'label'   => 'این سفارش هدیه است',
        'class'   => [ 'icsd-gift-toggle' ],
    ] );
    
    woocommerce_form_field( 'icsd_gift_message', [
        'type'        => 'textarea',
        'label'       => 'متن کارت هدیه',
        'placeholder' => 'پیام شما...',
        'required'    => false,
        'class'       => [ 'icsd-gift-message' ],
    ] );
}

// ذخیره در سفارش
add_action( 'woocommerce_checkout_update_order_meta', 'icsd_save_gift_data' );
function icsd_save_gift_data( $order_id ) {
    if ( ! empty( $_POST['icsd_is_gift'] ) ) {
        update_post_meta( $order_id, '_icsd_is_gift', '1' );
        update_post_meta( $order_id, '_icsd_gift_message', sanitize_textarea_field( $_POST['icsd_gift_message'] ?? '' ) );
    }
}

// نمایش در سفارش (پیشخوان و ایمیل)
add_action( 'woocommerce_admin_order_data_after_order_details', 'icsd_show_gift_admin' );
function icsd_show_gift_admin( $order ) {
    if ( get_post_meta( $order->get_id(), '_icsd_is_gift', true ) ) {
        $message = get_post_meta( $order->get_id(), '_icsd_gift_message', true );
        echo '

🎁 سفارش هدیه:

'; echo '
'; echo nl2br( esc_html( $message ) ); echo '
'; } }

۷. سفارشی‌سازی ایمیل سفارش

ووکامرس چه ایمیل‌هایی می‌فرستد؟

کلید گیرنده زمان ارسال
new_order ادمین سفارش جدید ثبت شد
cancelled_order ادمین سفارش لغو شد
failed_order ادمین پرداخت ناموفق
customer_on_hold_order مشتری سفارش در انتظار
customer_processing_order مشتری سفارش در حال پردازش
customer_completed_order مشتری سفارش تکمیل شد
customer_refunded_order مشتری سفارش مسترد شد
customer_invoice مشتری دستی توسط ادمین
customer_note مشتری یادداشت جدید
customer_new_account مشتری ثبت‌نام

سفارشی‌سازی قالب ایمیل

روش بازنویسی قالب
1. به مسیر افزونه ووکامرس بروید:
   wp-content/plugins/woocommerce/templates/emails/

2. فایل مورد نظر را کپی کنید (مثلاً customer-completed-order.php)

3. در قالب فعال خود، این مسیر را بسازید:
   wp-content/themes/your-theme/woocommerce/emails/

4. فایل را در آنجا پیست کنید و ویرایش کنید.

ووکامرس به صورت خودکار قالب شما را به جای پیش‌فرض استفاده می‌کند.

افزودن محتوای دینامیک به ایمیل

functions.php
// افزودن کد رهگیری به ایمیل completed
add_action( 'woocommerce_email_after_order_table', 'icsd_add_tracking_to_email', 10, 4 );

function icsd_add_tracking_to_email( $order, $sent_to_admin, $plain_text, $email ) {
    if ( $email->id !== 'customer_completed_order' ) return;
    
    $tracking = $order->get_meta( '_icsd_tracking_code' );
    if ( ! $tracking ) return;
    
    if ( $plain_text ) {
        echo "nnکد رهگیری پست: $trackingn";
        echo "لینک رهگیری: https://tracking.post.ir/?id=$trackingn";
    } else {
        ?>
        

📦 کد رهگیری بسته شما

🔍 پیگیری مرسوله

۸. عملیات روی سفارش

بازپرداخت با کد

refund-order.php
function icsd_refund_order( $order_id, $amount = null, $reason = '' ) {
    $order = wc_get_order( $order_id );
    
    if ( ! $order ) {
        return new WP_Error( 'invalid_order', 'سفارش پیدا نشد' );
    }
    
    $amount = $amount ?: $order->get_total();
    
    $refund = wc_create_refund( [
        'amount'         => $amount,
        'reason'         => $reason,
        'order_id'       => $order_id,
        'refund_payment' => false, // اگر API درگاه را داریم true کنید
    ] );
    
    if ( is_wp_error( $refund ) ) {
        return $refund;
    }
    
    $order->update_status( 'refunded', 'بازپرداخت توسط سیستم: ' . $reason );
    
    return $refund;
}

// مثال
icsd_refund_order( 123, 500000, 'مشتری راضی نبود' );

چاپ فاکتور با کد سفارش

invoice.php
function icsd_generate_invoice( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) return;

ob_start();
?>




فاکتور #<?php echo $order->get_order_number(); ?>


شماره: get_order_number(); ?>

تاریخ: get_date_created()->getTimestamp() ); ?>

اطلاعات مشتری

نام: get_formatted_billing_full_name(); ?>
تلفن: get_billing_phone(); ?>
آدرس: get_formatted_billing_address(); ?>

get_items() as $item ): ?>


محصول تعداد قیمت واحد مجموع
get_name() ); ?> get_quantity(); ?> get_subtotal() / $item->get_quantity() ); ?> get_total() ); ?>
جمع جزء get_subtotal() ); ?>
هزینه ارسال get_shipping_total() ); ?>
مالیات get_total_tax() ); ?>
مبلغ نهایی get_total() ); ?>

با تشکر از خرید شما
ICSD - icsd.ir

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.