نقش‌های وردپرس در سایت‌های کراودفاندینگ به‌دلیل ماهیت حمایت مالی عمومی، نیاز به مدیریت کمپین‌های متنوع، کنترل آستانه تأمین بودجه، و پیگیری تعهدات مالی، تنظیمات حمایتی اختصاصی می‌خواهند. یک حامی (Backer) نباید بتواند هویت سایر حامیان را ببیند. یک صاحب کمپین نباید بتواند مبلغ تأمین‌شده را دستکاری کند. یک اپراتور نباید به داده پرداخت دسترسی داشته باشد. بدون تنظیمات حمایتی، یا اعتماد به انصاف پلتفرم از بین می‌رود، یا داده مالی نشت می‌کند، یا کل سیستم کراودفاندینگ بی‌اعتبار می‌شود.

سایت کراودفاندینگ با چند لایه داده حساس سر و کار دارد: تعهد مالی، هویت حامی، داده پرداخت، و تاریخچه کمپین. هر لایه نیازمند سطح دسترسی متفاوتی است. مدل نقش پیش‌فرض وردپرس، این تفکیک را نمی‌شناسد. بدون تنظیمات حمایتی، خطر تقلب، نشت داده مالی، و از دست رفتن اعتماد کاربران وجود دارد. راه‌حل عملی، تعریف نقش‌های سفارشی، State Machine کمپین، و مدیریت اتمی تعهدات است. این مقاله معماری کامل این حوزه را بررسی می‌کند.

در پروژه‌ای که برای یک پلتفرم کراودفاندینگ طراحی می‌کردم، نخستین بحران از یک نکته کوچک شروع شد: صاحب کمپین توانست مبلغ تأمین‌شده را در صفحه نمایش به‌نفع خودش تغییر دهد. آن تجربه نشان داد که در کراودفاندینگ، هر عدد یک ادعای مالی است، نه یک نمایش ساده، و هر نقص در آن، می‌تواند به از دست رفتن اعتماد حامیان منجر شود.

چرا نقش‌های پیش‌فرض وردپرس کافی نیستند؟

وردپرس شش نقش پیش‌فرض دارد که حول مدیریت محتوا ساخته شده‌اند. در سایت کراودفاندینگ، این نقش‌ها با شکاف‌های زیر روبه‌رو می‌شوند:

  • نبود تفکیک تعهد مالی: هر تعهد یک رکورد مالی است که باید تغییرناپذیر باشد.
  • نبود آستانه‌سنجی اتمی: آستانه تأمین بودجه باید به‌صورت اتمی بررسی شود.
  • نبود مدیریت پاداش: هر تعهد ممکن است پاداش متناظر داشته باشد که باید با دقت توزیع شود.
  • ریسک تقلب: بدون کنترل دقیق، صاحب کمپین می‌تواند مبلغ را دستکاری کند.

در کراودفاندینگ، هر عدد یک ادعای مالی است که باید تغییرناپذیر باشد.

برای درک مفاهیم پایه نقش و دسترسی، مطلب مدیریت نقش‌ها و دسترسی‌ها در وردپرس را ببینید. اگر به انتخاب افزونه مدیریت کاربران فکر می‌کنید، مطلب انتخاب افزونه مدیریت کاربران وردپرس راهنمای دقیقی است.

نقش‌های ضروری در سایت کراودفاندینگ

حامی (Backer)

ثبت تعهد مالی، مشاهده تاریخچه تعهدات خودش، دریافت پاداش، بدون دسترسی به هویت سایر حامیان.

صاحب کمپین (Campaign Owner)

ایجاد کمپین، تعیین آستانه، مشاهده آمار تجمیعی، بدون تغییر مبلغ پس از شروع.

اپراتور کمپین (Campaign Operator)

نظارت بر انصاف کمپین، حل اختلاف، بدون دسترسی به داده پرداخت.

ناظر مالی (Financial Auditor)

دسترسی به داده پرداخت پس از پایان کمپین، بدون دسترسی به هویت حامیان.

مدیر پلتفرم (Platform Manager)

مدیریت کلی، گزارش تجمیعی، بدون دسترسی مستقیم به تعهدات.

چالش‌های حمایت مالی

پیش از ورود به پیاده‌سازی، باید چالش‌های خاص این حوزه را دقیق بشناسیم.

۱. آستانه‌سنجی اتمی

آستانه تأمین بودجه باید به‌صورت اتمی بررسی شود تا از Race Condition جلوگیری شود. این نیازمند یک لایه قفل سبک است. برای مطالعه بیشتر، مطلب پیاده‌سازی درست Custom Post Type را ببینید.

۲. مدیریت تعهدات

هر تعهد یک رکورد مالی است. تعهدات باید تغییرناپذیر باشند و انصراف باید به‌عنوان رویداد جداگانه ثبت شود. برای مطالعه بیشتر، مطلب کار با Options API در وردپرس را ببینید.

۳. توزیع پاداش

هر سطح تعهد ممکن است پاداش متناظر داشته باشد. توزیع پاداش باید پس از تأمین بودجه انجام شود. این نیازمند یک چرخه Fulfillment است.

۴. هویت حامی

برخی حامیان مایل به ناشناس ماندن هستند. سیستم باید بتواند هویت واقعی را در یک لایه جدا نگه دارد و فقط نام نمایشی را نشان دهد. برای مطالعه بیشتر، مطلب فیلدهای سفارشی ACF و کاربردهای واقعی آن را ببینید.

۵. بازپرداخت در صورت شکست

اگر آستانه تأمین نشود، پرداخت حامیان باید بازگردانده شود. این نیازمند یک چرخه بازپرداخت دقیق است.

تنظیمات حمایتی برای هر نقش

نقش ثبت تعهد مشاهده وضعیت ایجاد کمپین
حامی بله فقط خودش خیر
صاحب کمپین خیر آمار تجمیعی بله
اپراتور خیر کامل مدیریت
ناظر مالی خیر پس از پایان خیر
مدیر پلتفرم خیر گزارش تجمیعی مدیریت

پیاده‌سازی با PHP

ابتدا CPT کمپین کراودفاندینگ را می‌سازیم. برای مطالعه بیشتر، مطلب پیاده‌سازی درست Custom Post Type را ببینید.

function wk_register_crowdfunding_campaign_cpt() {
    register_post_type('wk_crowdfunding_campaign', array(
        'label' => 'کمپین‌های کراودفاندینگ',
        'public' => false,
        'show_ui' => true,
        'capability_type' => 'wk_crowdfunding_campaign',
        'capabilities' => array(
            'read_post' => 'read_wk_crowdfunding_campaign',
            'edit_post' => 'edit_wk_crowdfunding_campaign',
            'edit_posts' => 'edit_wk_crowdfunding_campaigns',
            'edit_others_posts' => 'edit_others_wk_crowdfunding_campaigns',
        ),
        'map_meta_cap' => true,
        'supports' => array('title', 'editor', 'author', 'custom-fields'),
    ));
}
add_action('init', 'wk_register_crowdfunding_campaign_cpt');

سپس نقش‌های سفارشی را می‌سازیم. برای درک بهتر هوک‌ها، مطلب هوک‌های وردپرس: قلب تپنده توسعه را ببینید.

function wk_register_crowdfunding_roles() {
    add_role('wk_backer', 'حامی', array(
        'read' => true,
        'read_wk_crowdfunding_campaigns' => true,
        'pledge_wk_campaign' => true,
        'read_own_wk_pledges' => true,
        'receive_wk_rewards' => true,
    ));

    add_role('wk_campaign_owner', 'صاحب کمپین', array(
        'read' => true,
        'edit_wk_crowdfunding_campaigns' => true,
        'publish_wk_crowdfunding_campaigns' => true,
        'read_wk_campaign_stats' => true,
    ));

    add_role('wk_campaign_operator', 'اپراتور کمپین', array(
        'read' => true,
        'manage_wk_campaigns' => true,
        'resolve_wk_disputes' => true,
        'read_all_wk_pledges' => true,
    ));

    add_role('wk_financial_auditor', 'ناظر مالی', array(
        'read' => true,
        'read_wk_payment_data' => true,
        'export_wk_financial_reports' => true,
    ));
}
add_action('init', 'wk_register_crowdfunding_roles');

برای اعتبارسنجی و پاک‌سازی داده‌های ورودی، مطلب اعتبارسنجی داده‌ها در کدنویسی وردپرس و پاک‌سازی داده‌ها در کدنویسی وردپرس را ببینید.

مدیریت اتمی تعهدات

هر تعهد باید اتمی ثبت شود و مبلغ تأمین‌شده به‌صورت پویا محاسبه شود:

function wk_place_pledge($user_id, $campaign_id, $amount, $reward_id = null) {
    if (!current_user_can('pledge_wk_campaign')) {
        return new WP_Error('forbidden', 'دسترسی غیرمجاز');
    }
    global $wpdb;
    $lock_key = 'wk_pledge_lock_' . $campaign_id;
    if (!wp_cache_add($lock_key, 1, '', 5)) {
        return new WP_Error('locked', 'سیستم مشغول است، دوباره تلاش کنید');
    }
    $state = get_post_meta($campaign_id, 'wk_campaign_state', true);
    if ($state !== 'active') {
        wp_cache_delete($lock_key);
        return new WP_Error('not_active', 'کمپین فعال نیست');
    }
    $min_pledge = (float) get_post_meta($campaign_id, 'wk_min_pledge', true);
    if ($amount < $min_pledge) {
        wp_cache_delete($lock_key);
        return new WP_Error('too_low', 'مبلغ کمتر از حداقل است');
    }
    $pledge_id = wp_insert_post(array(
        'post_type' => 'wk_pledge',
        'post_status' => 'publish',
        'post_author' => $user_id,
    ));
    update_post_meta($pledge_id, 'wk_campaign_id', $campaign_id);
    update_post_meta($pledge_id, 'wk_amount', $amount);
    update_post_meta($pledge_id, 'wk_reward_id', $reward_id);
    update_post_meta($pledge_id, 'wk_pledge_hash', hash('sha256', sprintf('%d|%d|%f|%d', $campaign_id, $user_id, $amount, time())));
    update_post_meta($pledge_id, 'wk_created_at', current_time('mysql'));
    $total = (float) get_post_meta($campaign_id, 'wk_total_pledged', true);
    update_post_meta($campaign_id, 'wk_total_pledged', $total + $amount);
    wp_cache_delete($lock_key);
    return $pledge_id;
}

نکته کلیدی: استفاده از wp_cache_add به‌عنوان قفل سبک و به‌روزرسانی مجموع پس از ثبت تعهد. برای مطالعه بیشتر، مطلب ترنزینت وردپرس و کش هوشمند بدون افزونه را ببینید.

State Machine کمپین

هر کمپین از چند وضعیت عبور می‌کند:

function wk_transition_crowdfunding_state($campaign_id, $new_state, $actor_id) {
    $allowed = array(
        'draft' => array('review', 'cancelled'),
        'review' => array('approved', 'rejected'),
        'approved' => array('active'),
        'active' => array('funded', 'expired', 'cancelled'),
        'funded' => array('fulfilling', 'disputed'),
        'fulfilling' => array('completed', 'disputed'),
        'expired' => array('refunded'),
        'disputed' => array('completed', 'refunded'),
    );
    $current = get_post_meta($campaign_id, 'wk_campaign_state', true);
    if (!isset($allowed[$current]) || !in_array($new_state, $allowed[$current], true)) {
        return new WP_Error('invalid_transition', 'انتقال وضعیت مجاز نیست');
    }
    update_post_meta($campaign_id, 'wk_campaign_state', $new_state);
    update_post_meta($campaign_id, 'wk_last_actor', $actor_id);
    update_post_meta($campaign_id, 'wk_state_changed_at', current_time('mysql'));
    return true;
}

امنیت و حریم خصوصی

سه لایه امنیتی ضروری است:

  1. ثبت تغییرناپذیر هر تعهد با Hash محتوا برای ممیزی.
  2. استفاده از قفل سبک برای جلوگیری از Race Condition در آستانه‌سنجی.
  3. محافظت از هویت حامیان ناشناس در یک لایه جدا با رمزنگاری.

برای مطالعه بیشتر درباره کدنویسی امن، مطلب نوشتن کد PHP امن برای وردپرس را ببینید. برای Nonce، مطلب نانس وردپرس و امنیت فرم را ببینید. برای امن‌سازی نشست‌ها، مطلب امن‌سازی نشست‌های کاربری را ببینید.

مرجع فنی این حوزه در ویکی‌پدیا، مقاله Crowdfunding مرور خوبی از تاریخ و مدل‌های کراودفاندینگ ارائه می‌دهد.

پرسش‌های پرتکرار درباره تنظیمات نقش کراودفاندینگ

آیا می‌توانم از نقش Subscriber برای حامی استفاده کنم؟

خیر. Subscriber فقط می‌تواند پست بخواند، اما هیچ مکانیزمی برای ثبت تعهد یا مدیریت پاداش ندارد. باید نقش سفارشی با Capability اختصاصی بسازید.

چگونه از تقلب صاحب کمپین جلوگیری کنم؟

با جداسازی نقش‌ها: صاحب کمپین نمی‌تواند تعهد ثبت کند و نمی‌تواند مبلغ تأمین‌شده را دستکاری کند. مبلغ باید از تجمیع تعهدات محاسبه شود.

چگونه هویت حامی ناشناس را محافظت کنم؟

با نگهداری هویت واقعی در یک جدول جداگانه با رمزنگاری، و نمایش فقط نام نمایشی در سطح عمومی.

چگونه بازپرداخت در صورت شکست را مدیریت کنم؟

با یک State Machine که وضعیت «expired» را به «refunded» منتقل می‌کند و یک چرخه پرداخت اتمی.

آیا تعهدات باید تغییرناپذیر باشند؟

بله. هر تعهد یک سند مالی است که باید تغییرناپذیر باشد. انصراف باید به‌عنوان رویداد جداگانه ثبت شود، نه حذف رکورد.

اشتباهات رایج

اشتباه پیامد راه‌حل
مبلغ تأمین‌شده قابل ویرایش توسط صاحب کمپین نقض انصاف پلتفرم محاسبه پویا از تجمیع تعهدات
حذف رکورد تعهد به‌جای رویداد عدم امکان ممیزی ثبت تغییرناپذیر با رویداد جداگانه
نقش یکسان برای حامی و صاحب کمپین تقلب احتمالی نقش سفارشی مجزا
نبود State Machine وضعیت نامعتبر کمپین State Machine با انتقال معتبر
نبود لاگ تعهدات عدم امکان استناد حقوقی ثبت هر تعهد در جدول اختصاصی

نگاه معماری در سطح سیستم‌های کراودفاندینگ

در سطح معماری، سایت کراودفاندینگ با یک چالش بنیادین روبه‌روست: هر تعهد یک ادعای مالی است که در طول کمپین انباشته می‌شود. یعنی نمی‌توان تعهدات را جدا از هم مدیریت کرد. این نیازمند یک معماری «Event-Sourced» است که هر تعهد را تغییرناپذیر ثبت کند.

رویکرد پیشنهادی، معماری «Event Sourcing + Reward Fulfillment» است. هر تعهد یک Event تغییرناپذیر است. مبلغ تأمین‌شده به‌صورت پویا از تجمیع رویدادها محاسبه می‌شود. پس از تأمین بودجه، یک Workflow جداگانه برای توزیع پاداش اجرا می‌شود.

نکته دوم، بحث «Escrow Pattern» است. پرداخت حامیان باید در یک حساب Escrow نگهداری شود و تنها در صورت تأمین بودجه به صاحب کمپین منتقل شود. این نیازمند یک لایه Orchestration با درگاه پرداخت است.

نکته سوم، بحث «Pseudonymous Backer Identity» است. حامیان ناشناس باید بتوانند با یک نام نمایشی شرکت کنند، اما هویت واقعی آن‌ها برای اهداف قانونی در یک لایه جدا با رمزنگاری نگهداری شود. این نیازمند یک مکانیزم Multi-Party Decryption است.

پیشنهادهای عملی برای پیاده‌سازی

سه توصیه عملی که در پروژه‌های واقعی مؤثر بوده‌اند:

  1. هر تعهد را با Hash محتوا و امضای زمانی ثبت کنید تا در صورت اختلاف، قابل استناد باشد.
  2. مبلغ تأمین‌شده را به‌صورت پویا از تجمیع تعهدات محاسبه کنید، نه از یک فیلد قابل ویرایش.
  3. یک Endpoint عمومی برای تأیید وضعیت کمپین طراحی کنید تا در صورت شکایت، قابل استناد باشد.

اگر این چالش‌ها را در یک پروژه واقعی تجربه کرده‌اید، برای ما جالب است بدانید کدام بخش بیشترین زمان را گرفت: مدیریت تعهدات، State Machine، یا چرخه بازپرداخت. تجربه خود را در دیدگاه‌ها بنویسید.