Woocommerce дополнительные платные опции на странице оформления заказа
Я использую этот код в WooCommerce для вывода дополнительных платных опций. Опция в блоке активируется галочкой в чекбоксе. После этого в выпадающем списке можно выбрать варианты этой опции с разными ценами. После выбора варианта, сумма заказа, без учёта стоимости доставки, автоматически увеличивается на экране. Код рассчитывает сумму в процентом отношении от суммы заказа. После подтверждения заказа данные появляются в Woocommerce - Заказы.
Вопрос:
- Как сделать так, что бы в выпадающем списке стоимость вариаций была фиксированная (НЕ в процентом отношении) и одна из опций была с возможностью вручную ввести сумму типа доната. И после ввода суммы итоговая сумма заказа, без учета стоимости доставки, также менялась автоматически на экране.
- Как сделать несколько таких блоков т.е несколько платных опций каждая из которых имеет вариации.

Код на github - https://gist.github.com/psaikali/728be703ac016235bf587051882521c5
Описание и инструкция по коду: https://saika.li/woocommerce-customize-checkout/
<?php
/**
* Plugin Name: WooCommerce Custom Checkout Fields
* Description: Add some custom "emergency level" extra fields on the WooCommerce Checkout page. Save this custom data in each order metadata.
* Author: Pierre Saïkali
* Author URI: https://saika.li
* Text Domain: wc_ccf
* Domain Path: /languages/
* Version: 1.0.0
* Full Tutorial: https://mosaika.fr/personnaliser-tunnel-commande-woocommerce/
*/
namespace WC_CCF;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
//========================================
//
// ## ## ###### ## ## ####
// ## ## ## ## ## ##
// ## ## ## ## ## ###
// ## ## ## ## ## ##
// ##### ## ## ###### ####
//
//========================================
/**
* Get form posted data from WooCommerce: in some cases, it's serialized in a "post_data" key.
*
* @return array
*/
function get_wc_posted_data() {
$form_data = $_POST;
if ( isset( $_POST['post_data'] ) ) {
parse_str( $_POST['post_data'], $form_data );
}
return $form_data;
}
//==========================================================
//
// #### ## #### ##### ## ### ## ##
// ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ### ##### ## ## ## ####
// ## ## ## ## ## ## ####### ##
// #### ## #### ## ###### ## ## ##
//
//==========================================================
/**
* Display our custom extra fields: a checkbox and a select dropdown.
*
* @return void
*/
function display_custom_shipping_methods() {
?>
<fieldset class="extra-fields">
<legend><?php _e( 'Emergency', 'wc_ccf' ); ?></legend>
<p>
<label for="msk-urgent-order">
<input type="checkbox" name="msk-urgent-order" id="msk-urgent-order" value="on" class="msk-custom-field" />
<span><?php esc_html_e( 'This is an emergency, send me my order ASAP!', 'wc_ccf' ); ?></span>
</label>
</p>
<p>
<label for="msk-urgency-level">
<span><?php esc_html_e( 'When do you want this shipped?', 'wc_ccf' ); ?></span>
<select name="msk-urgency-level" id="msk-urgency-level" class="msk-custom-field">
<option value="_null"><?php esc_attr_e( '— Select an option', 'wc_ccf' ); ?></option>
<option value="next_hour"><?php esc_attr_e( 'In the next hour', 'wc_ccf' ); ?></option>
<option value="next_6hours"><?php esc_attr_e( 'In the next 6 hours', 'wc_ccf' ); ?></option>
<option value="next_12hours"><?php esc_attr_e( 'In the next 12 hours', 'wc_ccf' ); ?></option>
<option value="tomorrow"><?php esc_attr_e( 'By tomorrow', 'wc_ccf' ); ?></option>
</select>
</label>
</p>
</fieldset>
<script>
// When one of our custom field value changes, tell WC to update the checkout data (AJAX request to the back-end).
jQuery(document).ready(function($) {
$('form.checkout').on('change', '.msk-custom-field', function() {
$('body').trigger('update_checkout');
});
});
</script>
<?php
}
add_action( 'woocommerce_checkout_billing', __NAMESPACE__ . '\\display_custom_shipping_methods', 10 );
//==================================================================
//
// ## ## ### ## ## #### ### ###### #####
// ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## #####
// ## ## ####### ## ## ## ## ####### ## ##
// ### ## ## ###### ## #### ## ## ## #####
//
//==================================================================
/**
* Validate checkout fields before processing the WooCommerce "order"
*
* @return void
*/
function validate_all_checkout_fields() {
$errors = [];
if ( isset( $_POST['msk-urgent-order'] ) && $_POST['msk-urgent-order'] === 'on' ) {
if ( ! isset( $_POST['msk-urgency-level'] ) || $_POST['msk-urgency-level'] === '_null' ) {
$errors[] = __( 'Please tell us the <strong>level of emergency</strong>.', 'wc_ccf' );
}
elseif ( isset( $_POST['msk-urgency-level'] ) && ! in_array( $_POST['msk-urgency-level'], [ 'next_hour', 'next_6hours', 'next_12hours', 'tomorrow' ], true ) ) {
$errors[] = __( 'This <strong>level of emergency</strong> is invalid.', 'wc_ccf' );
}
}
/**
* If we have errors,
*/
if ( ! empty( $errors ) ) {
foreach ( $errors as $error ) {
wc_add_notice( $error, 'error' );
}
}
}
add_action( 'woocommerce_checkout_process', __NAMESPACE__ . '\\validate_all_checkout_fields' );
//==============================================================================
//
// #### ### ##### ###### ##### ##### ## #### #####
// ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ##### ## ##### ##### ## ## #####
// ## ####### ## ## ## ## ## ## ## ## ##
// #### ## ## ## ## ## ## ## ## ## #### #####
//
//==============================================================================
/**
* Add emergency fee depending on the emergency level selected.
*
* @param WC_Cart $cart_object
* @return void
*/
function add_emergency_fee( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) || ! is_checkout() ) {
return;
}
// Only trigger this logic once.
if ( did_action( 'woocommerce_cart_calculate_fees' ) >= 2 ) {
return;
}
$form_data = get_wc_posted_data();
// Do not calculate anything if we do not have our emergency field checked or no emergency level is provided.
if ( ! isset( $form_data['msk-urgent-order'], $form_data['msk-urgency-level'] ) || $form_data['msk-urgent-order'] !== 'on' ) {
return;
}
// Store a mutiplier/coefficient to calculate the emergency fee.
$multipliers = [
'next_hour' => 2,
'next_6hours' => 1.5,
'next_12hours' => 1.25,
'tomorrow' => 1.1,
];
if ( ! array_key_exists( $form_data['msk-urgency-level'], $multipliers ) ) {
return;
}
// Add the extra fee to the user cart.
WC()->cart->add_fee(
__( 'Emergency processing fee', 'wc_ccf' ),
WC()->cart->subtotal * $multipliers[ $form_data['msk-urgency-level'] ],
false
);
}
add_action( 'woocommerce_cart_calculate_fees', __NAMESPACE__ . '\\add_emergency_fee' );
//========================================================================================
//
// ##### ##### #### ###### ##### ##### #### ##### #####
// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// ##### ## ## ### ## ######## ## ## ##### ## ## ##### #####
// ## ## ## ## ## ## ## ## ## ## ## ## ## ##
// ## ##### #### ## ##### ## ## #### ##### ## ##
//
//========================================================================================
/**
* For tutorial sake, enable the order process without payment.
*
* @param boolean $needed
* @return boolean
*/
function woocommerce_order_needs_payment( $needed ) {
return false;
}
add_filter( 'woocommerce_cart_needs_payment', __NAMESPACE__ . '\\woocommerce_order_needs_payment' );
/**
* Allow our custom checkout data (emergency checkbox & level) to be included in order data array.
*
* @param array $data
* @return array
*/
function add_custom_checkout_data_to_order_data_array( $data ) {
$custom_keys = [
'msk-urgent-order',
'msk-urgency-level',
];
foreach ( $custom_keys as $key ) {
if ( isset( $_POST[ $key ] ) ) {
$data[ $key ] = sanitize_text_field( $_POST[ $key ] );
}
}
return $data;
}
add_filter( 'woocommerce_checkout_posted_data', __NAMESPACE__ . '\\add_custom_checkout_data_to_order_data_array', 10, 2 );
/**
* Save our custom checkout data on the order metadata.
*
* @param integer $order_id
* @param array $data Posted data.
* @return void
*/
function save_custom_checkout_data_in_order_metadata( $order_id, $data ) {
$custom_keys = [
'msk-urgent-order',
'msk-urgency-level',
];
$order = wc_get_order( $order_id );
foreach ( $custom_keys as $key ) {
if ( isset( $data[ $key ] ) ) {
$order->add_meta_data( $key, $data[ $key ] );
}
}
$order->save();
}
add_action( 'woocommerce_checkout_update_order_meta', __NAMESPACE__ . '\\save_custom_checkout_data_in_order_metadata', 10, 2 );