
All deposit / split / partial payment plugins generate an additional order for paying the balance. Today, we change that.
Hosted by Rodolfo Melogli
Masterclass overview
It may happen you need to enable deposit payments, partial payments, multiple payment methods per order (e.g. half PayPal, half Bank), installments, split an order between more customers, or whatever requires the customer to pay a percentage of the order total and then come back to pay the difference, possibly with a different payment method.
Most, maybe all, plugins out there, complete the current order upon partial payment and create another order for the balance (a “sub-order” i.e. an order linked to the one that was partially paid), so that the customer can complete that one at a given date.
This is because WooCommerce requires a 1:1 relationship between orders and payments, and does not allow you to use multiple “transactions” for the same order.
Speaking of which, welcome to the world of “transactions“. By using this custom post type, we can save each payment on its own (partial, deposit, partial refund, installment), and then associate multiple transactions to a single order, so that the order status and total are dynamically set based on the outstanding balance.
A customer can then pay for the same orders multiple times, and each time the order amount will be defined by the total sum of its associated transactions.
In this class, we will take a look at some PHP code and demo it live, so that you can get the full picture of this – really! – revolutionary WooCommerce customization.
This is an amazing opportunity to see how a simple yet effective functionality can be coded, so that you can learn a new thing or two for your WooCommerce website or your WooCommerce clients. It’s also a great opportunity to hang out with like-minded professionals during the live class.
Recording & Materials
Video recording
If you are a member, please log in.
Otherwise, here is why you should join the Club.
Code used during the class
add_action( 'init', function() {
$supports_array = array( 'title' );
$args = array(
'labels' => array(
'name' => 'Payments',
'singular_name' => 'Payments'
),
'capability_type' => 'shop_order',
'public' => false,
'supports' => $supports_array,
'rewrite' => false,
'show_ui' => true,
'show_in_menu' => current_user_can( 'edit_others_shop_orders' ) ? 'woocommerce' : false,
'menu_position' => 3,
);
register_post_type( 'payment', $args );
});
add_action( 'woocommerce_order_after_calculate_totals', function( $and_taxes, $order ) {
$payments = get_posts( [ 'post_type' => 'payment', 'post_status' => 'publish', 'numberposts' => -1, 'title' => $order->get_id(), 'fields' => 'ids' ] );
if ( $payments ) {
$total = $order->get_total();
$paid = 0.00;
foreach ( $payments as $payment_id ) {
$amount = get_field( "amount", $payment_id );
$paid += $amount;
}
$new_total = $total - $paid;
$order->set_total( $new_total );
if ( $new_total > 0 ) $order->set_status( 'pending' );
$order->save();
}
}, 9999, 2 );
add_action( 'acf/save_post', function( $post_id ) {
if ( get_post_type( $post_id ) !== 'payment' && get_post_status( $post_id ) !== 'publish' ) return;
$order = wc_get_order( get_the_title( $post_id ) );
if ( ! $order ) return;
$order->calculate_totals();
});
add_action( 'woocommerce_thankyou', function( $order_id ) {
$order = wc_get_order( $order_id );
if ( $order->has_status( 'failed' ) ) return;
$my_post = array(
'post_title' => $order_id,
'post_status' => 'publish',
'post_type' => 'payment',
);
$new_post_id = wp_insert_post( $my_post );
update_field( 'amount', $order->get_total(), $new_post_id );
$order->calculate_totals();
});
add_action( 'woocommerce_review_order_before_payment', function() {
$chosen = WC()->session->get( 'deposit_chosen' );
$chosen = empty( $chosen ) ? WC()->checkout->get_value( 'deposit' ) : $chosen;
$chosen = empty( $chosen ) ? '100' : $chosen;
$args = array(
'type' => 'radio',
'class' => array( 'form-row-wide', 'update_totals_on_change' ),
'options' => array(
'100' => 'Pay 100%',
'30' => 'Pay 30% Deposit',
),
'default' => $chosen,
);
echo '<p><b>Payment Option</b></p>';
woocommerce_form_field( 'deposit', $args, $chosen );
}, 1 );
add_action( 'woocommerce_checkout_update_order_review', function( $posted_data ) {
parse_str( $posted_data, $output );
if ( isset( $output['deposit'] ) ) {
WC()->session->set( 'deposit_chosen', $output['deposit'] );
}
});
add_filter( 'woocommerce_calculated_total', function( $total, $cart ) {
if ( ! WC()->session->get( 'deposit_chosen' ) || WC()->session->get( 'deposit_chosen' ) == '100' ) return $total;
return $total * 0.3;
}, 9999, 2 );
add_action( 'woocommerce_admin_order_totals_after_total', function ( $order_id ) {
echo '<tr><td class="label">Payments:</td><td width="1%"></td><td class="total"><ul style="margin:0">';
$payments = get_posts( [ 'post_type' => 'payment', 'post_status' => 'publish', 'numberposts' => -1, 'title' => $order_id, 'fields' => 'ids' ] );
if ( $payments ) {
foreach ( $payments as $payment_id ) {
$amount = get_field( "amount", $payment_id );
echo '<li style="margin:0"><a href="' . get_edit_post_link( $payment_id ) . '">' . wc_price( $amount ) . '</a> (' . get_the_time( 'd-m-y', $payment_id ) . ')</li>';
}
}
echo '</ul></td></tr>';
});
What you’ll learn
Requirements
Upcoming masterclasses
How to Spin Up WooCommerce Test Websites For Free
Testing WooCommerce snippets, plugins, themes shouldnβt be a hassle. Let’s discover free…
Live Migrating a WooCommerce Site to HPOS
Migrating a WooCommerce site to HPOS can be complex, especially with 40,000+…
Generate WooCommerce Test Data: Products, Orders, Users
To properly test or develop a WooCommerce site, you need a large…
Available recordings
Log Events & Debug Custom Code with WooCommerce Logger
WooCommerce provides a simple way to log your custom events and debug your custom code…
Optimize WooCommerce Performance with WordPress Transients
Discover how WordPress transients can boost WooCommerce speed! Weβll explain what transients are, their pros…
Unlocking the Power of WooCommerce Featured Products
Many developers and store owners find themselves unsure about how to effectively use featured products….
Buying a WooCommerce Store: All You Need to Know
A guide to valuation, negotiation, and acquisition strategies – along with post-acquisition tips for optimizing…
1-Hour WooCommerce Challenge: Let’s Recreate the Nike Product Page
Join me for a live coding challenge, where I’ll customize the WooCommerce Single Product page…
Live Coding a WooCommerce Mini-Plugin
Join me for a live coding session, while I try to develop a custom, commercial…
WooCommerce AMA with Rodolfo Melogli
Join me for an ‘Ask Me Anything About WooCommerce’ session β covering customization, development, plugins,…
Maximize Your WooCommerce Potential: Understanding User Behavior with Clarity
Learn how to use the free Microsoft Clarity plugin to record and analyze user behavior…
Mastering WooCommerce Thank You Page Customization: A Plugin-Free Approach
Let’s learn how to personalize the WooCommerce Thank You page with simple code, so that…
– BACKED BY –
Is your WooCommerce store prepared for traffic spikes? Improve speeds up to 200% with our
managed WooCommerce hosting. Enjoy scalable server resources, rock-solid security, and 24/7 support.
Thatβs awesome. I was on clueless how to accept multiple payments.
Thank you mate
Great!
Great tutorial, Rodolfo!
Thank you!
This was helpful, but felt like it was missing the major piece to tie it all together, which would be to send a order request to a customer with a preset deposit amount. While this accomplished the deposit by first using the checkout page, it doesn’t let shop owners send a pay order request (invoice) to a customer with the down payment request.
I would have loved to see the deposit radio fields added to the order page on possibly the ‘before_woocommerce_pay_form’ hook, or even better allow the site owner to choose the deposit amount to send the order, instead of requiring a end user checkout to trigger this.
Any advice on how to achieve this? Overall great video.
Thank you Bryan!
Good point! My solution only applies to checkout orders and not orders created via admin. However, you can always display the radio buttons (“Pay deposit” / “Pay in full”) on the Order Pay page as well, and that will let the customer choose between a set amount and a 100% payment.
I’m sure there’s also a way to implement the preset deposit amount right from the admin edit order page – this can be custom coded: you enter an amount, click “request payment”, and the Order Pay page asks for the preset deposit instead of the full amount.
The smooth alternative may be a URL parameter! Let’s say you want to force a $5 deposit – simply add “&dep=5” to the Order Pay URL (e.g. example.com/checkout/order-pay/123456/?pay_for_order=true&key=wc_order_xyz&dep=5) and install this snippet:
Let me know!