Commerce partial payment : pay deposit at checkout and full settlement later In a Travel Booking website, how can I configure checkout so that customers pay only a n% deposit of the order total at checkout (off-site payment ATOS solution) ?

If the order is created :


less than 30 days before departure, customer pay 100% of order total at checkout.
between 30 and 90 days before, they pay 30% at checkout.
more than 90 days before, they pay 10% at checkout.


Full settlement must be paid before travel departure (reminder emails)...

There are few contrib modules about deposit or partial payment :


I had a look at Commerce deposit sandbox but it seems to be a per product solution, while I need a global cart approach.
I began to hack Commerce Partial Payment for my own need. The module alters checkout_form_alter and provides a textfield where the customer can input a deposit value just before payment, so I pre-calculated deposit amount as per my rules above, filled the field with it, and hide it.

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 *
 * Alters whichever commerce checkout page includes the commerce_payment
 * checkout pane and adds the amount element to the payment details.
 */
function commerce_partial_payment_form_commerce_checkout_form_alter(&amp;$form, &amp;$form_state, $form_id) {

  $panes = commerce_checkout_panes(array('pane_id' =&gt; 'commerce_payment'));
  $checkout_page = $panes['commerce_payment']['page'];
  if ($form_id != "commerce_checkout_form_$checkout_page" || !commerce_partial_payment_access($form_state['order'])) {
    return;
  }

  //setting up my own deposit value
  $deposit = vts_commerce_deposit();

  // If a values has already been set then use it as the value for the amount.
  // Otherwise, default it to the balance of the order.
  $default = NULL;

  if ($deposit &gt; 0) {
     $default = $deposit ;
  }
  elseif (isset($form_state['values']['commerce_payment']['payment_details']['amount'])) {
    $default = $form_state['values']['commerce_payment']['payment_details']['amount'];
  }
  else {
    $balance = commerce_payment_order_balance($form_state['order']);
    $default = commerce_currency_amount_to_decimal($balance['amount'], $balance['currency_code']);
  }

  // Add the amount to the payment details form.
  $form['commerce_payment']['payment_details']['amount'] = array(
    '#type' =&gt; 'hidden',
    //'#title' =&gt; t('Amount'),
    '#weight' =&gt; -10,
    '#required' =&gt; TRUE,
    '#default_value' =&gt; $default,
  );
}



But I got stuck there because that value,  passed to $form['commerce_payment']['payment_details']['amount'],  is not the one sent to the remote payment server (so that AFAIK, this module can't work)...

I also tried to set any other parts of checkout_form_alter ( $form and $form_state) where the order amount was repeated with $deposit, this NEVER changes the value set for payment at next step...

Question(s)

Using the above solution, at what point of checkout / which form can I alter the amount to be paid, replacing order total by deposit value ?

Indeed, I couldn't find how to separate the order total from the amount to pay... with correct data along the process : order total / paid deposit / balance order to pay later.

If there is a more D7 commerce way to implement this deposit issue, with rules or whatever, I'll be glad to learn it.
I got it working the following way.


First install Commerce Partial Payment.
Write a custom function to calculate deposit, MODULENAME_deposit_calculation


In order to get Commerce Deposit work with the deposit calculation you like to instead of a customer deposit input, patch it as following :

function commerce_partial_payment_form_commerce_checkout_form_alter(&amp;$form, &amp;$form_state, $form_id) {
  //... line 93
  $acompte = MODULENAME_deposit_calculation($form_state['order']) /100;
    if (!is_null($acompte) &amp;&amp; $acompte &gt; 0) {
      $default = $acompte;
    }
  //... line 109 Add the amount to the payment details form,in a hidden field.
  $form['commerce_payment']['payment_details']['amount'] = array(
    '#type' =&gt; 'hidden',
  //...
}

function commerce_partial_payment_checkout_validate($form, &amp;$form_state, $checkout_pane, $order) {
  //... line 261
  $amount['amount'] = MODULENAME_deposit_calculation($order)  /100;
  //...
}

function commerce_partial_payment_checkout_submit($form, &amp;$form_state, $checkout_pane, $order) {
  //...line 317
  if ($pane_values['payment_methods']) {
    $amount = MODULENAME_deposit_calculation($order) /100;
    $order-&gt;data['atos']['request']['amount'] = $amount;
    if (!empty( $amount)){
      $amount['amount'] = $amount;
  //...
}


As the sandbox doesn't properly send the deposit amount to off site payment, we need to hook the payment method :

In a METHOD_PAYMENT.api.php file

function hook_METHOD_PAYMENT_request_alter(&amp;$settings) {
  //no example
}


Now send to off site payment the deposit amount instead of order total :

function commerce_partial_payment_PAYMENT_METHOD_request_alter(&amp;$settings) {
  $order = commerce_order_load($settings['order_id']);
  $deposit = MODULENAME_deposit_calculation($order);
  $settings['amount'] = $deposit;
}


Last, add a price component deposit below Total to inform the customer what he's going to pay. 

function commerce_partial_payment_commerce_price_formatted_components_alter(&amp;$components, $price, $entity) {
  //Add a deposit line to price components
  $deposit= MODULENAME_deposit_calculation($entity);

  $components['deposit'] = array(
      'title' =&gt; t('Deposit'),
      'price' =&gt; array(
        'amount' =&gt; $deposit,
        'currency_code' =&gt; $components['commerce_price_formatted_amount']['price']['currency_code'],
        'data' =&gt; '',
      ),      
    'weight' =&gt; '200',
  );
}