Something like that:
// 1. Display multiple upload file field
add_action( 'woocommerce_after_order_notes', 'add_custom_checkout_field' );
function add_custom_checkout_field($checkout) {
echo '<div class="woocommerce-additional-fields__field-wrapper">';
woocommerce_form_field('certificate', array(
'type' => 'file',
'class' => array('form-row-wide'),
'label' => __('Files', 'woocommerce'),
'required' => false,
'multiple' => 'multiple',
'name' => 'certificate[]', // as array
'accept' => '.pdf,.doc,.docx,.rtf,.txt',
), '');
echo '</div>';
}
// 2. Save multiple uploaded files URL and name to order meta
add_action( 'woocommerce_checkout_create_order', 'save_checkout_uploaded_files', 10, 2 );
function save_checkout_uploaded_files( $order, $data ){
if( !empty($_FILES['certificate']['name'][0]) ) {
$uploaded_files = array();
foreach ($_FILES['certificate']['name'] as $key => $value) {
if ($_FILES['certificate']['error'][$key] === UPLOAD_ERR_OK) {
$file = array(
'name' => $_FILES['certificate']['name'][$key],
'type' => $_FILES['certificate']['type'][$key],
'tmp_name' => $_FILES['certificate']['tmp_name'][$key],
'error' => $_FILES['certificate']['error'][$key],
'size' => $_FILES['certificate']['size'][$key]
);
// Handle upload safely using WP functions
$upload = wp_handle_upload($file, array('test_form' => false));
if (!isset($upload['error'])) {
$uploaded_files[] = array(
'file_url' => $upload['url'],
'file_name' => $file['name']
);
}
}
}
if (!empty($uploaded_files)) {
$order->update_meta_data( '_checkout_upload', $uploaded_files );
}
}
}
// 3. Helper function to display uploaded files as links
function display_uploaded_files_list($files) {
if (!empty($files) && is_array($files)) {
echo '<p>' . __("Files Uploaded:", 'woocommerce') . '</p><ul>';
foreach ($files as $file) {
printf('<li><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></li>', esc_url($file['file_url']), esc_html($file['file_name']));
}
echo '</ul>';
}
}
// 4. Display uploaded files in admin order page
add_action('woocommerce_admin_order_data_after_billing_address', 'display_uploaded_files_in_admin_orders');
function display_uploaded_files_in_admin_orders( $order ) {
$uploaded_files = $order->get_meta( '_checkout_upload' );
display_uploaded_files_list($uploaded_files);
}
// 5. Display uploaded files on thank you page
add_action('woocommerce_order_details_after_order_table', 'display_uploaded_files_in_thankyou');
function display_uploaded_files_in_thankyou( $order ) {
$uploaded_files = $order->get_meta( '_checkout_upload' );
display_uploaded_files_list($uploaded_files);
}
// 6. Display uploaded files in WooCommerce emails
add_action('woocommerce_email_customer_details', 'display_uploaded_files_in_email');
function display_uploaded_files_in_email( $order ) {
$uploaded_files = $order->get_meta( '_checkout_upload' );
display_uploaded_files_list($uploaded_files);
}
____
WooCommerce’s woocommerce_form_field does not natively support the HTML5 multiple attribute for file inputs, and by default, it will use name="certificate" than name="certificate[]" so:
Render the file input with custom HTML:
add_action( 'woocommerce_after_order_notes', 'add_custom_checkout_field' );
function add_custom_checkout_field($checkout) {
echo '<div class="woocommerce-additional-fields__field-wrapper">';
echo '<p class="form-row form-row-wide">';
echo '<label for="certificate">' . __('Files', 'woocommerce') . '</label>';
echo '<input type="file" name="certificate[]" id="certificate" multiple accept=".pdf,.doc,.docx,.rtf,.txt" />';
echo '</p></div>';
}
Handle multi-file upload in the WooCommerce order hook:
add_action( 'woocommerce_checkout_create_order', 'save_checkout_uploaded_files', 10, 2 );
function save_checkout_uploaded_files( $order, $data ){
if( !empty($_FILES['certificate']['name'][0]) ) {
$uploaded_files = array();
foreach ($_FILES['certificate']['name'] as $i => $name) {
if ($_FILES['certificate']['error'][$i] === UPLOAD_ERR_OK) {
$file = array(
'name' => $_FILES['certificate']['name'][$i],
'type' => $_FILES['certificate']['type'][$i],
'tmp_name' => $_FILES['certificate']['tmp_name'][$i],
'error' => $_FILES['certificate']['error'][$i],
'size' => $_FILES['certificate']['size'][$i]
);
$upload = wp_handle_upload($file, array('test_form' => false));
if (!isset($upload['error'])) {
$uploaded_files[] = array(
'file_url' => $upload['url'],
'file_name' => $file['name']
);
}
}
}
if (!empty($uploaded_files)) {
$order->update_meta_data( '_checkout_upload', $uploaded_files );
}
}
}
and display all uploaded files:
function display_uploaded_files_list($files) {
if (!empty($files) && is_array($files)) {
echo '<p>' . __("Files Uploaded:", 'woocommerce') . '</p><ul>';
foreach ($files as $file) {
printf('<li><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></li>', esc_url($file['file_url']), esc_html($file['file_name']));
}
echo '</ul>';
}
}
add_action('woocommerce_admin_order_data_after_billing_address', function($order) {
$files = $order->get_meta('_checkout_upload');
display_uploaded_files_list($files);
});
add_action('woocommerce_order_details_after_order_table', function($order) {
$files = $order->get_meta('_checkout_upload');
display_uploaded_files_list($files);
});
add_action('woocommerce_email_customer_details', function($order) {
$files = $order->get_meta('_checkout_upload');
display_uploaded_files_list($files);
});
____
edit 2 - this should work:
1: Add multiple file input field on checkout
Add this code to your theme’s functions.php or a custom plugin. It inserts a multiple file input after the order notes.
add_action( 'woocommerce_after_order_notes', 'add_custom_checkout_field' ); function add_custom_checkout_field( $checkout ) {
echo '<div class="woocommerce-additional-fields__field-wrapper">';
echo '<p class="form-row form-row-wide">';
echo '<label for="certificate">' . __( 'Files', 'woocommerce' ) . '</label>';
echo '<input type="file" name="certificate[]" id="certificate" multiple accept=".pdf,.doc,.docx,.rtf,.txt" />';
echo '</p></div>';
}
- This creates a file input that allows selecting multiple files.
The input name certificate[] ensures files are submitted as an array.
2: Handle and save uploaded files to order meta
Add code to process uploaded files when the order is created.
add_action( 'woocommerce_checkout_create_order', 'save_checkout_uploaded_files', 20, 2 );
function save_checkout_uploaded_files( $order, $data ) {
if ( ! empty( $_FILES['certificate']['name'][0] ) ) {
$uploaded_files = [];
foreach ( $_FILES['certificate']['name'] as $i => $name ) {
if ( $_FILES['certificate']['error'][ $i ] === UPLOAD_ERR_OK ) {
$file = [
'name' => $_FILES['certificate']['name'][ $i ],
'type' => $_FILES['certificate']['type'][ $i ],
'tmp_name' => $_FILES['certificate']['tmp_name'][ $i ],
'error' => $_FILES['certificate']['error'][ $i ],
'size' => $_FILES['certificate']['size'][ $i ],
];
$upload = wp_handle_upload( $file, [ 'test_form' => false ] );
if ( ! isset( $upload['error'] ) ) {
$uploaded_files[] = [
'file_url' => $upload['url'],
'file_name' => $file['name'],
];
}
}
}
if ( $uploaded_files ) {
$order->update_meta_data( '_checkout_upload', $uploaded_files );
$order->save(); // Save meta data
}
}
}
Loop through each uploaded file.
Use WordPress function wp_handle_upload to save files securely.
Save an array of file data (URL and name) in order meta _checkout_upload.
3: Display uploaded files list (helper function)
Create a reusable function to output uploaded file links.
function display_uploaded_files_list( $files ) {
if ( ! empty( $files ) && is_array( $files ) ) {
echo '<p>' . __( 'Files Uploaded:', 'woocommerce' ) . '</p><ul>';
foreach ( $files as $file ) {
printf(
'<li><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></li>',
esc_url( $file['file_url'] ),
esc_html( $file['file_name'] )
);
}
echo '</ul>';
}
}
4: Show uploaded files on the admin order details page
add_action( 'woocommerce_admin_order_data_after_billing_address', function( $order ) {
$files = $order->get_meta( '_checkout_upload' );
display_uploaded_files_list( $files );
} );
5: Show uploaded files on the Thank You page
add_action( 'woocommerce_order_details_after_order_table', function( $order ) {
$files = $order->get_meta( '_checkout_upload' );
display_uploaded_files_list( $files );
} );
6: Show uploaded files in WooCommerce order emails
add_action( 'woocommerce_email_customer_details', function( $order ) {
$files = $order->get_meta( '_checkout_upload' );
display_uploaded_files_list( $files );
} );
7: Test
Place a new order.
On checkout, select multiple files using the file input.
Complete the order.
Check in WordPress admin Orders page if files appear under billing address.
Review the Thank You page after checkout for uploaded files links.
Check WooCommerce order emails for file links.
Confirm your server PHP settings allow uploading files of your desired size (upload_max_filesize, post_max_size).
Clear any caches or object cache that may hide updated order meta.
'custom_attributes' => array('multiple'),does not work. It seems to me that the source code of the file needs to be slightly modified checkout_uploads.php?