New User Creation at Checkout and automatic role grant

Project:Ubercart Contributions
Component:Code
Category:bug report
Priority:critical
Assigned:Unassigned
Status:active
Description
Project: 
uc_roles2

New User Checkout is not functioning with CAs that allow a role to be immediately granted. I flipped through lots of issues on this, but they were all pertaining to UC 1.x

I run a site that offers subscriptions and other items for sale. Ubercart has been fantastic so far, thank you very much.

I want users to immediately receive their premium role upon purchase of a subscription, so I set a conditional action that would move the order from Payment Received to Completed if the weight of the item is 0. I also experimented with setting the role to be applied upon Payment Received rather than Order Completed.

When an anonymous user creates an account at checkout and these actions are set I get the following error in my log:
Duplicate entry '' for key 2 query: INSERT INTO sslab_users (created) VALUES (1254156607) in /home/sporbcom/public_html/modules/user/user.module on line 327.

In this case, Ubercart will report Customer granted user role Elite Member on the order page, but the user will not actually be granted the role. I suspect that the workflow is a little out of order, and UC tries to grant the role to an account that doesn't exist yet?

This has forced me to turn off the automatic subscription start until I find another way to do it...certainly not optimal. Is this a bug? is there another workaround I haven't encountered or considered?

Thank you for the support-

Version: 
Ubercart 2.x-dev
vood002's picture
Offline
Joined: 08/06/2009
Juice: 55
forgot to mention

I'm using UC 2.0 RC7, Drupal 6.14, and the most recent dev of uc_recurring.

I said I flipped through issues on this for 1.x...What I meant was similar issues.

seanh's picture
Offline
Joined: 10/26/2009
Juice: 9
Re: New User Creation at Checkout and automatic role grant
Assigned to:vood002» seanh

I can reproduce this as well. with rc6 in production.

haven't really starting getting into it, but it certainly makes for an annoying new user workflow (at the moment we work around it by not allowing anon cart access)

roglesby's picture
Offline
Joined: 01/12/2010
Juice: 6
This Problem Continues
Assigned to:seanh» roglesby

Running Drupal v6.15 and Ubercart v 6.x-2.2 on checkout using the mock gateway an anonymous subscriber gets the dreaded error message and no special role assignment. Examining the order, it says the special role has been assigned and an email is sent out to this effect. 3 weeks of experimenting with the features aspect of this product, conditional actions, exploring Drupal and Ubercart documentation has not gotten me any closer to solving the problem. Has anyone found a cause/solution to this problem?

As has been suggested elsewhere disabling anonymous check out fixes the problem but does add an extra page to the check out process and will diminish sales.

Thanks for any help you can give.
Robert

higgins's picture
Offline
Joined: 06/17/2009
Juice: 4
same here. I think roles
Assigned to:roglesby» higgins

same here.

I think roles worked properly before I started deleting users. Anyone found a solution yet?

update - 9:16 EST: I suppose it has something to do with the user (uid = 0). Deleted this user (uid = 0), and broke my cart!

Thanx,
higgins

scot.self's picture
Offline
Joined: 02/11/2010
Juice: 107
#5
Project:Replacement for uc_roles» Ubercart Contributions
Category:task» bug report
Priority:normal» critical
Assigned to:higgins» Guest

Same here. Same error message, shows the recurring subscription assigned to "anonymous". Please help as this is keeping us from going to production. Thanks for everything!! See screen shots showing checkout successful (with error message), new user's account page with no recurring fees showing (permission is enabled), and expiration assigned to anonymous user instead of the user created at checkout. Thanks in advance for the help! I will be forced to manually assign the role every time someone purchases a subscription if not!!

AttachmentSize
order complete.JPG 28.02 KB
no roles on user profile.JPG 4.6 KB
role expiration assigned to anon.JPG 20.43 KB
scot.self's picture
Offline
Joined: 02/11/2010
Juice: 107
#6

also note that the emails to the user are generated indicating the role was granted when it was not. don't know if this is relevant or not, but that email comes BEFORE the email with the users' new account login details.

scot.self's picture
Offline
Joined: 02/11/2010
Juice: 107
#7

bump. please help!

scot.self's picture
Offline
Joined: 02/11/2010
Juice: 107
#8

I've gone the "don't allow anonymous checkout" route on this too, but would love to know if this ever gets solved...thanks.

vood002's picture
Offline
Joined: 08/06/2009
Juice: 55
#9

I've poked around a lot with this code, but i just can't see what exactly is going wrong here. I bet it's something simple and someone more familiar with uc_role than myself will be able to fix it...in the meantime I've written a temporary stopgap that checks for broken subscriptions on each cron run and activates roles where necessary. Please note that this code could certainly be cleaner, and you will have to change it a tiny bit to get it to work for you...also I haven't tested it in production, so we'll see...but hopefully this helps someone. Please note that you'll want to change the role activation email to something along the lines of "Your account will be activated within the hour", and I'm also granting roles on payment_received now rather than on complete.

<?php
function my_module_cron(){
 
$recNids = array(21280,21570); //Product NIDs that contain recurring fees. There are better ways to detect this, but this way is easy and fast.
 
$cronDuration = time() - 3600; //Your Cron Duration in seconds (3600 = 1 hour)
 
  //Select all orders since last cron that are completed or payment received
 
$query = "SELECT `order_id` FROM {uc_orders} WHERE `created` >= %d AND `order_status` in ('completed','payment_received')";
 
$result = db_query($query,$cronDuration);
 
  while (
$oid = db_fetch_array($result)){
   
$recProd = NULL;
   
$order = uc_order_load($oid);
    foreach(
$order->products as $product){
      if (
in_array($product->nid ,$recNids) ){
           
//There is a recurring fee in this order, record which one and continue
             
$recProd = $product->model;
          }
      }
     
     
//There is a recurring fee on this order, check uc_role to see if user was granted role
     
if (isset($recProd)){
       
$ucRole = NULL;
       
$query = "SELECT * FROM {uc_roles_expirations} WHERE `uid` = %d";
         
$result2 = db_query($query,$order->uid);
         
$ucRole = db_fetch_array($result2);
         
         
//User has not been granted the role! Grant it now.
         
if (!$ucRole){
           
$prod_role = db_fetch_array(db_query("SELECT `duration`,`granularity` FROM {uc_roles_products} WHERE `model` = %s"),$recProd);
             
$expiry = strtotime('+' . $prod_role['duration'] . ' ' . $prod_role['granularity']);
             
$account = user_load($order->uid);
           
uc_roles_grant($account, 6, $expiry, TRUE, TRUE);
                   
//6 is my RID, you'll want to change this to whatever you want. You can grab it from the uc_roles_products  table if you need, I just skipped this step.
         
}
      }   
  }
}
?>
kindafun's picture
Offline
Joined: 05/12/2010
Juice: 18
#10

I've got the same problem and need to fix it! Assigning a role to the new user. I get the warning:

user warning: Duplicate entry '' for key 'name' query: INSERT INTO users (created) VALUES (1273991378) in ...

Having to create an account first is terrible for most uses so I have to think that most don't have this problem. Doing something with the account, like assigning the role, must be the issue.

vessenes's picture
Offline
Joined: 04/02/2009
Juice: 15
#11

Bump. I would be willing to offer a small bounty for a fix for this.

vessenes's picture
Offline
Joined: 04/02/2009
Juice: 15
#12

I was thinking in the $100-ish range.

Gwink's picture
Offline
Joined: 09/17/2009
Juice: 9
#13

Anyone have an answer to this yet?

lgb
lgb's picture
Offline
Joined: 05/29/2009
Juice: 23
#14

@vood002

Great work on trying to solve this bizarre and unexpected bug. We just recently decided to convert our checkout to an anon-enabled workflow and this is just...weird.

I'm working on adapting your code into a Rule that will do something similar--every cron run, get those roles get properly assigned, and we wipe out these ghost anon users.

I spent some time with the code as well to brainstorm a proper fix, but couldn't figure this out. Maybe an additional dummy user is being created when the 'new user save' is happening in uc_cart.module (around line 1223 in uc-2.3). But it's definitely not obvious from the code that this is happening.

Would love to see an Ubercart genius slay this!!

lgb
lgb's picture
Offline
Joined: 05/29/2009
Juice: 23
#15

I've implemented a non-code workaround to correct this issue in my project. I'm posting it here in the hopes that it might help some of the folks affected by this.

The problem for me was that any order (free orders, no-approval orders, etc.) that was able to immediately run through the process to the "Complete" status was getting that role assignment stolen by the "anon" / duplicate user. Something that would interrupt the workflow seemed necessary.

I'm short on time so I'll outline my fix briefly. Happy to provide more details as needed.

1.) Created a new order state called "Pre-complete" to fit in between "Payment Received" and "Complete".
2.) The CA that was previously changing order status (of orders meeting certain criteria) to "Complete" is now only changing them to "Pre-complete". (Just fyi, a separate CA is handling the actual role assignment for "Complete" orders.)
3.) Workflow is interrupted sufficiently to circumvent this monstrous weirdness.
4.) A simple Bulk Operations view was created, capable of toggling the status of orders from Pre-complete to Complete.
5.) That view is being executed by a triggered Rule upon each cron run.
6.) The status change triggers the role assignment (from the second CA) to the proper user.

Hope it helps someone!

gsl
gsl's picture
Offline
Joined: 08/04/2010
Juice: 1
#16

I have a "sort of" solution using conditional actions. There are two cases: 1). new "anonymous" users making a purchase with a successful payment; and 2). all other purchases (existing users making a purchase, new users whose order doesn't complete on checkout). In the latter case, the original predicate works (apply role on order status change to Completed) because the user's account exists. In the former case, it fails because the new user's account hasn't been created when the order is completed (by a successful payment).

For the original roles predicate ("Grant or renew purchased roles" under the "Order status changed" trigger) I added a condition to disallow anonymous users and a "Execute custom PHP code" action to set a 'renewed' flag in the updated order's data array.

I then added a copy of the roles predicate under the "Customer completes checkout" trigger. The conditions require the order status to be Completed and the order not to have the 'renewed' flag (so I don't get duplicate role applications when an existing user makes a successful purchase). The action is just the "Renew the roles on an order" action.

So in case 1 (new user, successful purchase) the original predicate is ignored (because the order's user is anonymous) and the new predicate is used because the first predicate didn't set the 'renewed' flag. In case 2 (existing user, order status becomes Completed) the original predicate is used and the new predicate is ignored (because the 'renewed' flag was set).

The site I am working on involves membership with role expiration so the grant/renew roles action can only be performed once per order. I imagine if you don't need roles to expire, you can do away with the 'renewed' flag and let the action occur twice (if the user's account exists) which should be harmless.

As for how to fix the actual problem, I don't know. The sequence of events that I see (for credit card payment using the test gateway) is:

  1. From the order review pane, click the "Submit order" button which calls uc_cart_checkout_review_form_submit()
  2. which calls the hook_order() 'submit' hook on all modules including uc_credit_order
  3. which calls uc_payment_process()
  4. which, if successful, calls uc_payment_enter()
  5. which fires the "A payment gets entered for an order" CA trigger
  6. which calls the "Update order status on full payment" predicate
  7. which changes the order status to Completed
  8. which fires the "Order status gets updated" trigger
  9. which tries to apply the role to the anonymous user account and throws an error
  10. while a bit later, after all the hook_order calls, uc_cart_checkout_complete() is called
  11. which calls uc_cart_complete_sale which creates the account for the new user (and fires the "Customer completes checkout" trigger).

Maybe the new user's account should be created on successful payment rather than deferred until checkout complete.

Geoff.

pfahlr's picture
Offline
Joined: 09/20/2010
Juice: 9
#17

Following gsi's logic, I created this patch to uc_role.module (6.x - 2.4). This isn't the most elegant solution, but it works. What I've done is move the user creation from uc_cart.module into an implementation of hook_order() in uc_role.module on the 'save' operation. This moves user creation to the form step prior to checkout, so the user is created and logged in when they get to the "Review Order" page. It might be possible to move this into the 'submit' operation of hook_order, but I honestly didn't explore the possibility much. It failed so I tried 'save'. Perhaps adjusting the module weights would allow this.

AttachmentSize
uc_roles.module.patch 2.96 KB
straubse's picture
Offline
Joined: 11/04/2010
Juice: 4
#18

I may be missing something in reading this thread, but I have a similar setup with UC2.x & D6.19 and this works for me. Anon checkout that creates users on payment receipt and assigns a role for x amount of time to access a subscription site.

Also of note is you can setup a rule to assign a role based on various actions/conditions like account creation. Maybe not ideal if you need multiple roles, but could be helpful for some. Again, mine works without and in addition to the rule.

RandyT's picture
Offline
Joined: 03/20/2011
Juice: 9
#19

Sorry to revive this slightly old thread...

I am trying to accomplish the same using the conditional actions. The hints I have found in these forums and on the web suggest a bit of code that does not use the two available variables that the conditional actions says I have access to, $order and $updated_order

Here is the code, any help to massage this would be appreciated:

if ($account) {
  $uid = $account->uid;
  $role_name = 'authenticated user';
  $rid = db_result(db_query("SELECT rid FROM {role} WHERE name = '%s'", $role_name));
  db_query("INSERT INTO {users_roles} (uid, rid) VALUES(%d, %d)", $uid, $rid);
  watchdog('user', 'uc ca added role to Ubercart created user');
}
RandyT's picture
Offline
Joined: 03/20/2011
Juice: 9
#20

So I have been using the following bit of code, but it is not working. I am also at a loss for how to debug this stuff in UC. Any suggestions would be welcome.

$account = uc_order_user_load($order);

if ($account) {
  $uid = $account->uid;
  $role_name = 'donor-one';
  $rid = db_result(db_query("SELECT rid FROM {role} WHERE name = '%s'", $role_name));
  db_query("INSERT INTO {users_roles} (uid, rid) VALUES(%d, %d)", $uid, $rid);
  watchdog('user', 'uc ca added role to Ubercart created user');
}