This document describes how to add custom checkout fields (Purchase Order Reference & User Note) and a 2.7% credit card processing fee for Stripe payments in FluentCart.
- Purchase Order Reference Field: Optional text field on checkout
- User Note Field: Optional textarea for order notes
- Stripe Processing Fee: 2.7% fee automatically added to Stripe payments only
- Email Shortcodes: Use
{{order.purchase_order_reference}},{{order.user_note}}, and{{order.processing_fee}}in email templates - Order Display: Fields and fee displayed in admin and customer order details
Add the following code to /app/Hooks/actions.php:
// Add fields to checkout form
add_filter('fluent_cart/checkout_page_name_fields_schema', function($nameFields, $context) {
$cart = $context['cart'] ?? null;
$purchaseOrderRef = '';
$userNote = '';
if ($cart) {
$formData = ($cart->checkout_data ?? [])['form_data'] ?? [];
$purchaseOrderRef = $formData['purchase_order_reference'] ?? '';
$userNote = $formData['user_note'] ?? '';
}
$nameFields['purchase_order_reference'] = [
'id' => 'purchase_order_reference',
'name' => 'purchase_order_reference',
'type' => 'text',
'data-type' => 'text',
'label' => __('Purchase Order Reference', 'fluent-cart'),
'required' => 'no',
'placeholder' => __('PO Number (Optional)', 'fluent-cart'),
'value' => $purchaseOrderRef,
];
$nameFields['user_note'] = [
'id' => 'user_note',
'name' => 'user_note',
'type' => 'textarea',
'data-type' => 'textarea',
'label' => __('Order Notes', 'fluent-cart'),
'required' => 'no',
'placeholder' => __('Add any special instructions or notes...', 'fluent-cart'),
'value' => $userNote,
'rows' => 3,
];
return $nameFields;
}, 10, 2);
// Save to order meta
add_action('fluent_cart/order_created', function($order) {
$cart = \FluentCart\App\Models\Cart::query()->where('order_id', $order->id)->first();
if ($cart) {
$formData = ($cart->checkout_data ?? [])['form_data'] ?? [];
if (!empty($formData['purchase_order_reference'])) {
$order->updateMeta('purchase_order_reference', sanitize_text_field($formData['purchase_order_reference']));
}
if (!empty($formData['user_note'])) {
$order->updateMeta('user_note', sanitize_textarea_field($formData['user_note']));
}
}
}, 10, 1);
// Display in order details
add_action('fluent_cart/admin_order_details_after_billing', function($order) {
$poRef = $order->getMeta('purchase_order_reference');
$userNote = $order->getMeta('user_note');
if ($poRef || $userNote) {
echo '<div class="fluent-cart-order-custom-fields" style="margin-top: 20px; padding: 15px; background: #f9f9f9; border-radius: 4px;">';
echo '<h3 style="margin-top: 0;">' . __('Additional Information', 'fluent-cart') . '</h3>';
if ($poRef) echo '<p><strong>' . __('Purchase Order Reference:', 'fluent-cart') . '</strong> ' . esc_html($poRef) . '</p>';
if ($userNote) echo '<p><strong>' . __('Order Notes:', 'fluent-cart') . '</strong><br>' . nl2br(esc_html($userNote)) . '</p>';
echo '</div>';
}
}, 10, 1);
add_action('fluent_cart/customer_order_details_after_billing', function($order) {
$poRef = $order->getMeta('purchase_order_reference');
$userNote = $order->getMeta('user_note');
if ($poRef || $userNote) {
echo '<div class="fluent-cart-order-custom-fields" style="margin-top: 20px; padding: 15px; background: #f9f9f9; border-radius: 4px;">';
echo '<h3 style="margin-top: 0;">' . __('Additional Information', 'fluent-cart') . '</h3>';
if ($poRef) echo '<p><strong>' . __('Purchase Order Reference:', 'fluent-cart') . '</strong> ' . esc_html($poRef) . '</p>';
if ($userNote) echo '<p><strong>' . __('Order Notes:', 'fluent-cart') . '</strong><br>' . nl2br(esc_html($userNote)) . '</p>';
echo '</div>';
}
}, 10, 1);
// Email shortcodes
add_filter('fluent_cart/email_shortcode_order', function($shortcodes, $order) {
$shortcodes['purchase_order_reference'] = esc_html($order->getMeta('purchase_order_reference') ?: '');
$shortcodes['user_note'] = nl2br(esc_html($order->getMeta('user_note') ?: ''));
return $shortcodes;
}, 10, 2);// Helper function
function fluent_cart_apply_stripe_processing_fee($order, $transaction = null) {
if ($order->payment_method !== 'stripe') return 0;
$processingFee = $order->getMeta('processing_fee', 0);
if ($processingFee <= 0) {
$processingFee = (int)round($order->subtotal * 0.027);
if ($processingFee > 0) {
$order->updateMeta('processing_fee', $processingFee);
$order->updateMeta('processing_fee_rate', 0.027);
$order->total_amount += $processingFee;
$order->save();
if ($transaction) {
$transaction->total = $order->total_amount;
$transaction->save();
}
}
}
return $processingFee;
}
// Calculate fee on order creation
add_action('fluent_cart/order_created', function($order) {
if ($order->payment_method === 'stripe') {
fluent_cart_apply_stripe_processing_fee($order);
}
}, 5, 1);
// Add fee to payment intent (onsite payments)
add_filter('fluent_cart/payments/stripe_onetime_intent_args', function($intentData, $context) {
$order = \FluentCart\Framework\Support\Arr::get($context, 'order');
$transaction = \FluentCart\Framework\Support\Arr::get($context, 'transaction');
if ($order && $order->payment_method === 'stripe') {
$fee = fluent_cart_apply_stripe_processing_fee($order, $transaction);
if ($fee > 0 && isset($intentData['amount'])) {
$currency = $intentData['currency'] ?? $order->currency;
$feeToAdd = \FluentCart\App\Helpers\CurrenciesHelper::isZeroDecimal($currency)
? (int)round($fee / 100)
: (int)$fee;
$intentData['amount'] = (int)$intentData['amount'] + $feeToAdd;
}
}
return $intentData;
}, 10, 2);
// Add fee to checkout session (hosted payments)
add_filter('fluent_cart/payments/stripe_checkout_session_args', function($sessionData, $context) {
$order = \FluentCart\Framework\Support\Arr::get($context, 'order');
$transaction = \FluentCart\Framework\Support\Arr::get($context, 'transaction');
if ($order && $order->payment_method === 'stripe') {
$fee = fluent_cart_apply_stripe_processing_fee($order, $transaction);
if ($fee > 0 && isset($sessionData['line_items'][0]['price_data']['unit_amount'])) {
$currency = $order->currency;
$feeToAdd = \FluentCart\App\Helpers\CurrenciesHelper::isZeroDecimal($currency)
? (int)round($fee / 100)
: (int)$fee;
$sessionData['line_items'][0]['price_data']['unit_amount'] += $feeToAdd;
}
}
return $sessionData;
}, 10, 2);
// Display fee in order summary
add_filter('fluent_cart/order_summary_items', function($items, $order) {
$fee = $order->getMeta('processing_fee', 0);
if ($fee > 0 && $order->payment_method === 'stripe') {
$rate = number_format($order->getMeta('processing_fee_rate', 0.027) * 100, 2);
$items[] = [
'label' => sprintf(__('Credit Card Processing Fee (%s%%)', 'fluent-cart'), $rate),
'value' => \FluentCart\App\Helpers\Helper::toDecimal($fee, true, $order->currency),
'type' => 'fee'
];
}
return $items;
}, 10, 2);
// Email shortcode support
add_filter('fluent_cart/email_order_data', function($orderData, $order) {
$fee = $order->getMeta('processing_fee', 0);
if ($fee > 0 && $order->payment_method === 'stripe') {
$rate = number_format($order->getMeta('processing_fee_rate', 0.027) * 100, 2);
$orderData['processing_fee'] = \FluentCart\App\Helpers\Helper::toDecimal($fee, true, $order->currency);
$orderData['processing_fee_label'] = sprintf(__('Credit Card Processing Fee (%s%%)', 'fluent-cart'), $rate);
}
return $orderData;
}, 20, 2);Use these shortcodes in email templates:
{{order.purchase_order_reference}}- Purchase Order Reference{{order.user_note}}- User Note (formatted with line breaks){{order.processing_fee}}- Processing Fee (formatted currency, only shows for Stripe)
Edit the rate in the helper function:
$processingFee = (int)round($order->subtotal * 0.027); // Change 0.027 to your rate (e.g., 0.03 for 3%)
$order->updateMeta('processing_fee_rate', 0.027); // Update this tooChange 'required' => 'no' to 'required' => 'yes' in the field definitions.
Fields are added to the name/email section. To add to billing section instead, use:
add_filter('fluent_cart/checkout_renderer/billing_fields', function($billingFields, $context) {
// Same field definitions as above
return $billingFields;
}, 10, 2);- Processing fee is calculated from subtotal (before discounts, shipping, tax)
- Fee is stored in cents (FluentCart's internal format)
- Fee is only applied to Stripe payments (not cash/offline)
- Fee is added to the payment amount before it's sent to Stripe
- All amounts are automatically converted for zero-decimal currencies (JPY, etc.)
- Create a test order with Stripe payment
- Verify fee appears in order total
- Check Stripe dashboard to confirm charge amount includes fee
- Verify fields display in admin and customer order details
- Test email shortcodes in email templates
$fee = 400;
/**
* 1) Include the fee in displayed "estimated total".
*/
add_filter('fluent_cart/cart/estimated_total', function ($total, $data) use ($fee) {
return (int) $total + (int) $fee;
}, 20, 2);
/**
* 2) Render a line in the checkout summary (before Total row).
*/
add_action('fluent_cart/checkout/before_summary_total', function ($data) use ($fee) {
?>
<li data-fct-test-gateway-fee-row>
<span class="fct_summary_label"><?php echo esc_html__('Payment Processing Fee', 'fluent-cart'); ?></span>
<span class="fct_summary_value"><?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($fee)); ?></span>
</li>
<?php
}, 9, 1);
/**
* Block-based checkout summary footer does NOT call `fluent_cart/checkout/before_summary_total`.
* So we inject the same `<li>` into the footer block output.
*/
add_filter('render_block_fluent-cart/checkout-summary-footer', function ($block_content) use ($fee) {
// If already injected, do nothing
if (strpos($block_content, 'data-fct-test-gateway-fee-row') !== false) {
return $block_content;
}
$row = '<li data-fct-test-gateway-fee-row>'
. '<span class="fct_summary_label">' . esc_html__('Payment Processing Fee', 'fluent-cart') . '</span>'
. '<span class="fct_summary_value">' . esc_html(\FluentCart\App\Helpers\Helper::toDecimal($fee)) . '</span>'
. '</li>';
// Prefer inserting right before the Total row, if present
$pos = strpos($block_content, 'class="fct_summary_items_total"');
if ($pos !== false) {
return substr($block_content, 0, $pos) . $row . substr($block_content, $pos);
}
// Fallback: insert before closing </ul>
return str_replace('</ul>', $row . '</ul>', $block_content);
}, 10, 1);