I want to set the $order->uid to a newly created user's uid in a payment submit handler but whenever the handler returns FALSE and payment fails, the form is regenerated, I get the expected error, but the shopping cart contents disappear. The order however looks like it's ready for processing again.

The functionality is that anonymous user's can buy products. I'm creating a Drupal account for them right away so I can later use their uid in the payment gateway unique order_id (The default Rules won't work as I need it immediately). Should I avoid changing the uid for the order? but I was hoping to use that to store the newly created uid. I guess I could just store it in a variable and then delete it or something.

What about form_state['storage'] type of functionality with commerce payment submit forms? What is the best practice there?

Comments

blasthaus’s picture

Ok so now with a bit of research, I think I found a solution for storage. It seems like I must create a new table to store the user's uid along with a unique order_id (for payment gateway) and the order ID along with a status field. It's a bit cumbersome, bc I will have to check this table if the form is submitted a second time. In the end I believe it's still possible to alter the order->uid but only after a successful transaction. Is this ok to do? Also the commerce_customer_profile uid needs to be altered as well since it will be 0 for anonymous users.

Presently I see no other nifty alternatives to storing any values upon form submit as no variables are passed by reference. It would be great to have something we could use for these kind of special case scenarios. I want to allow anonymous users the ability to check out, then upon the first form submission attempt, create their account and store their uid somewhere. As I mentioned, I want this uid in order to stash it in the unique order_id for the payment gateway transaction. In the meantime for anyone else in this boat here's the direction I'm headed:

doesn't work

<?php
function your_module_submit_form_submit ($payment_method, $pane_form, $pane_values, $order, $charge) {
  // if order is anonymous, create a user with user->uid;
  if ($user->uid != $order->uid) {
    $order->uid = $user->uid;
    commerce_order_save($order);
  }
  // if transaction fails, returning false will return the form (below) with nothing visible in the shopping cart contents
  if ($transaction == 'FAILURE') {
    drupal_set_message ('Your transaction failed...');
    return FALSE;
  }
  ...
}
function your_module_sumbmit_form ($payment_method, $pane_form, $pane_values, $order, $charge) {
    $order = commerce_order_load($order->order_id);
    ...

}
?>

here's the direction I'm headed (taken from drupal.org/project/commerce_ezypay)

<?php
function my_module_schema() {
  $schema = array();
  $schema['my_module_customer_number'] = array(
    'description' => 'Stores my payment gateway customer numbers',
    'fields'      => array(
      'commerce_order_id' => array(
        'description'   => 'Commerce Order Id',
        'type'          => 'int',
        'size'          => 'medium',
        'not null'      => TRUE,
      ),
      'uid' => array(
        'description'   => 'User ID',
        'type'          => 'int',
        'size'          => 'medium',
        'not null'      => TRUE,
      ),
      'customer_number' => array(
        'description'   => 'Customer number sent to payment gateway',
        'type'          => 'int',
        'size'          => 'big',
        'not null'      => TRUE,
      ),
      'status' => array(
        'description'   => 'Status whether a successful transaction or a failure',
        'type'          => 'int',
        'size'          => 'tiny',
        'not null'      => TRUE,
        'default'       => 0
      ),
    ),
    'primary key' => array('commerce_order_id'),
  );
return $schema;
}

function my_module_submit_form_submit ($payment_method, $pane_form, $pane_values, $order, $charge) {

  // first check to see if the $order->order_id is in our table

  if ($result = my_module_load_record($order->order_id) {
    $customer_number = $result['customer_number'];
    $user = user_load($result['uid']);
  }
  else {
    // this means the submit function is being run for the first time so create the user
    $user = ($order->uid != 0)  ?  user_load($order->uid) : my_module_create_user(trim($order->mail));
    $customer_number = $user->uid . '-' . time();
    
    // now save this in our table
    $record = array(
      'commerce_order_id' => $order->order_id,
      'uid' => $user->uid,
      'customer_number' => $customer_number,
      'status' => 0
    );
    drupal_write_record('my_module_customer_number', $record);
  }
  // now we have the same $user and $customer_number no matter how many failed attempts are made

  //  create a transaction
  $transaction = my_module_send_transaction($payment_method, $order, $charge, $user, $customer_number);

  if ($transaction == FALSE) {
    drupal_set_message ('Your transaction failed...');
    return FALSE;
  }
  else {
    // change the order to reflect the correct user
    $order->uid = $user->uid;
    commerce_order_save($order);

    // change the billing_profile to reflect the correct user
    // Store the profile ID for the related field as specified on the settings form.
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
    $billing_pane_id = 'customer_profile_billing';
    if ($field_name = variable_get('commerce_' . $billing_pane_id . '_field', '')) {
      $profile = $order_wrapper->{$field_name};
    }
    else {
      // Or try the association stored in the order's data array if no field is set.
      if (!empty($order->data['profiles'][$billing_pane_id])) {
        $profile = commerce_customer_profile_load($order->data['profiles'][$billing_pane_id]);
      }
    }
    if ($profile && !empty($profile->profile_id)) {
      $profile->uid = $user->uid;
      // Save the profile.
      commerce_customer_profile_save($profile);
    }
    // now we can make a transaction and update the above database record with a status of 1 (complete)
    // ...
  }
}

?>
blasthaus’s picture

Status: Active » Closed (works as designed)

So just reporting back that this is not a bug but something bugging me, sorry! The above code after $order_wrapper does not work. However what does work is the default rule Assign an anonymous order to a pre-existing user triggers once the order completes checkout and compares the email of the order with the Drupal user table and the order->uid and all the profile and billing references get updated to reflect the user if there is a match. I may note here that it is theoretically possible to have more than one Drupal account with the same email, esp if you are creating any user programmatically (like myself) so I deal with that in validation. Whew! I just wish I had known that 3 days ago :-)

One thing to note was I had to adjust the rule Send an order notification e-mail weight to be lower than the one above (along with any other rules acting on this event, otherwise if someone changed their email after a failed transaction it would fire too soon and send to the initial email address entered for some reason.

Marking this as closed.

I will mention for the record that I had to deal with several special cases in the submit_form handler in order to accommodate the for anonymous users the ability to buy subscription products where the subscription product requires a Drupal user->uid. This creates a bit of code bloat on the function, but everything does work. I will be posting a commerce module soon with this code if anyone is interested. What happens is we set up an account for the email address on the form upon the first form-submit, but store their "status" during the checkout process. Here are the special use cases I was able to solve:

  1. anonymous user credit card fails, then logs in later (via password reset) using the same email and then successfully checks out
  2. anonymous user credit card fails, and then changes their email address and then successfully checks out
  3. anonymous user credit card fails, and then changes email and logs in and then successfully checks out
  4. anonymous user credit card fails, then logs in with different email and then successfully checks out