Splitting off from #1375216: Anonymous users see other anoynmous user's credit card details.
Problem/Motivation
Anonymous users don't have a chance to 'use my existing card' for obvious reasons. However it is a desired feature that anonymous users should be able to complete checkout and have the card details they entered associated with their newly created account (which is created by the commerce checkout rules).
Proposed resolution
Add an order id column to the card on file table, which stores the order id associated with the first checkout with those card details. Create new checkout rules that run after checkout and update the user id column of the card on file table to use the new user id for the matching order id of the completed checkout.
Remaining tasks
Review the attached patches
User interface changes
None
API changes
Modules implementing card on file storage (saving card details to the db) need to provide the order id as well.
Comments
Comment #1
Heine CreditAttribution: Heine commentedIMO commerce_cardonfile_update_anonymous should also call the payment provider to enable it to update necessary data. An example of such data is the Customer ID kept by Authorize.Net's Customer Information Manager.
Comment #2
rickmanelius CreditAttribution: rickmanelius commented@Heine
Unless I'm mistaken, I believe the card on file module only stores a reference to the credit card profile and/or communicates through the merchant API and doesn't actually store the raw credit card information (which would be a HUGE PCI compliance mess). What this patch would do is essentially create create a temporary mapping to the order_id until the checkout is completed and THEN associate it with the full uid.
Comment #3
Heine CreditAttribution: Heine commentedI did not say anything about credit card data.
The authorize.net CIM also stores a "merchant user id". Card on file uses the uid of the drupal user for this. Atm this will be 0 for anon users, even with the patch. The payment modules should be informed of the association change and be able to update this ID (not sure if / how Auth.net supports this).
Comment #4
westwesterson CreditAttribution: westwesterson commentedSame patch as in main comment, with new files included in patch.
Comment #5
westwesterson CreditAttribution: westwesterson commentedComment #6
Chadwick Wood CreditAttribution: Chadwick Wood commentedI just tried out the patch in #5, and it didn't quite work.
When I used
patch < patch_commit_6e3913c95c94.patch
to run the patch, it all succeeded except for the update to commerce_cardonfile.info.But I just went in and manually made that edit. However, applying the patch didn't successfully tie the card on file data to the newly created account. The patch DID add the order_id column, and it did create the associated Rule to tie the card data to the user account associated with the order.
Looking at my database, the cardonfile data entered when I completed checkout has an order_id value of 0, so I'm guessing the problem is there. I'm looking at the patch itself, and I don't see where the code is that stores the order_id with the cardonfile row in the first place. So maybe that's what's missing? Or am I misreading it?
Comment #7
Chadwick Wood CreditAttribution: Chadwick Wood commentedOH! Maybe that's in the hook_schema implementation? I'm not an expert on that stuff. HOWEVER, I do think I see that the patch adds the foreign key on INSTALL, but on UPDATE, it only adds the order_id, not the foreign key. I ran this patch as an update, so that might explain the problem?
Comment #8
Chadwick Wood CreditAttribution: Chadwick Wood commentedI tried uninstalling and re-installing the module, and this didn't make a difference. A second order still generated a row in commerce_card_data with an order_id of 0.
Looking more into this, wouldn't this feature require you to alter the way cardonfile data is getting saved in other modules? For instance, I'm using commerce_authnet, and that's the module it looks like that's actually saving the card on file data, so that's where the order_id would need to get written, right?
Comment #9
Chadwick Wood CreditAttribution: Chadwick Wood commentedOk, I just modified my copy of commerce_authnet to write order_id to the saved card info table. After doing that, this patch works! But it seems there's no way to really test this patch WITHOUT modifying a module that works with it, so I don't know what that means for the review process.
Comment #10
BerdirThe definition should contain unsigned => TRUE, to match the order id column of cmmerce_order.
No need for the Implements docblock, update hooks should just have a short description of what they're doing. Also, tables are usually inside {} in comments.
The field schema needs to be hardcoded, or it will result in conflicts in case 7002 attempts to change the field definition.
And there is no need to add a return message unless it's something important for the user, something that he must do or so.
No need for the file name after @file and the author/license tags are usually not added in contrib projects. You are credited in the commit message.
Suggestion: "Rules action callback; Associate...". On a single line if possible..
The object after $order has no meaning.
Comment #11
dwkitchen CreditAttribution: dwkitchen commentedMoving to 2.x branch, will review soon.
Comment #12
univate CreditAttribution: univate commentedI have re-rolled this patch and tested on the current 2.x branch.
Obviously the method used here is a bit hacky (saving the order id an then fixing the uid when the account is finally created), but it works and I cant really think of a better to achieve saving credit cards for anonymous users.
Comment #13
univate CreditAttribution: univate commentedSorry meant to upload this patch, ignore previous.
Comment #14
dwkitchen CreditAttribution: dwkitchen commentedThanks @univate sorry I missed this when you posted it and the patch no longer applies.
I'm just trying to work on some other issues and then I can come back to this.
Comment #15
nicksanta CreditAttribution: nicksanta commentedRerolled against latest 7.x-2.xPlease test patch in #16
Comment #16
nicksanta CreditAttribution: nicksanta commentedUghh... missed a file. Ignore patch from #15 and test this one.
Comment #17
dwkitchen CreditAttribution: dwkitchen commentedThanks for the patch, I have had chance to have a look though it.
I see that the order_id being added to the card on file properties has NOT_NULL = TRUE, however a card on file does not have to be created through the checkout process and customer could use the add form if the payment gateway implements the callback.
In commerce_cardonfile_anonymous() use EntityFieldQuery rather than db select to get the card on file id.
It's better to define the default rule using the rule object rather than using the JSON import. See the default_rules in the recurring sub module.
Comment #18
stewart.adam CreditAttribution: stewart.adam commentedPatch attached with changes requested from #16. If this gets committed please credit nicksanta, I only changed a few lines :)
Comment #19
stewart.adam CreditAttribution: stewart.adam commentedOops, forgot to set to needs review.
Comment #20
stewart.adam CreditAttribution: stewart.adam commentedHm, looks like I mixed up the latest dev and git. Here's a copy that doesn't reproduce the dwkitchen's commits from earlier today.
(sorry for the redundant emails, followers!)
Comment #21
bendikrb CreditAttribution: bendikrb commented@ #20; where did the default_rules hook implementation go..?
Comment #22
Anonymous (not verified) CreditAttribution: Anonymous commented@ #3, Is there a solution to this problem?
Comment #23
deggertsen CreditAttribution: deggertsen commentedMarking this as needs work since it seems there are some pending questions that need responses, particularly #21. I have not reviewed this patch, but have been using a rule to force people to log in instead. However, this patch obviously appears to be a better solution so I am anxious to see it get applied and am willing to test once the above questions are answered.
Just in case anybody else would like to force login for a recurring payment order here is my rule:
I'm also cleaning up the issue files so it is clear to newcomers which patch needs review.
Comment #24
mglamanReroll of patch #20 to include rules_default.inc from #18 against current dev.
Comment #25
mglamanJust realized function in rules.inc did not match that in rules_default.inc. Fixed in this patch, tested successfully for anonymous users.
Comment #26
mglamanForgot to mark as needs review.
Comment #27
andyg5000Added missing rules_default file required for this to work. Thanks Matt!
Comment #28
andyg5000Comment #29
caspervoogt CreditAttribution: caspervoogt commentedJust tested https://drupal.org/files/issues/commerce_cardonfile-save-profiles-anonym... from #28 and is working beautifully.
Comment #30
j2r CreditAttribution: j2r commentedTested the patch #28 and it is working fine.
Thanks @andyg5000
Comment #31
mglamanI know this is being used on a production site, @andyg5000 revised and is using, @plethoradesign & @j2r say it works, so marking RBTC!
Comment #32
torgosPizzaNot sure if it matters, but according to the Drupal docs, if this is being rolled against the 2.x version, shouldn't the update hook be 7200?
Current patch has commerce_cardonfile_update_7105() as the update_n hook.
Comment #33
andyg5000Good find @torgospizza. We welcome an updated patch if you have time.
Comment #34
mglamanError when applying against latest dev
Reroll in process..
Comment #35
mglamanHere is reroll of patch.
Comment #36
bojanz CreditAttribution: bojanz commentedIf you're using cardonfile (and / or have a recurring use case), you shouldn't have anonymous checkout at all.
Even with this patch, if the user is not logged in, he won't be able to see his existing cards, and he might create a duplicate card, which is bad UX.
Also, Heine is right, cardonfile isn't mapping the user id to a remote customer id at the moment (#2259529: Map the uid to a remote customer_id), but it will have to, in order to have correct
address behavior. However, many payment methods (Stripe, Braintree) require you to create a customer before creating the card,
which means that you can't create the card unless you know the user.
Because of this limitation, I am marking the issue as "won't fix".
Comment #37
mindaugasd CreditAttribution: mindaugasd commentedI think, users should be able to register on his first checkout. And login to his account if he wants to change his plan later on. I think this is very important.
My site is build this way, so I guess, I cannot use beta3 :(
Comment #38
mindaugasd CreditAttribution: mindaugasd commentedPossibly I could create an account programmically before user makes his first checkout :) So if this is really non-fix, even for the first checkout, It is possible to hack around this :)
Comment #39
stefank CreditAttribution: stefank commentedWhat I have done is to create a new user before completing order(Prepare the transaction for sending to payment gateway). This means that when the checkout is completed then the card is saved to existing user. Also for saving duplicate card I have a rule which checks the user cards before completing checkout based on order email.
Comment #40
mglamanAfter reading bojanz's comment it makes sense. It's up to the site builder to discover a way to create user before checkout finishes so that Card on File can properly save cards. Otherwise the module would be stepping out of its scope. Not to mention it would be providing methods of saving cards which do not work with some payment providers.
Comment #41
bojanz CreditAttribution: bojanz commentedExactly.
There is nothing stopping you from creating a user account as soon as the user enters his email and hits submit.
Commerce anonymous checkout is a bit broken and in the recurring use case there is no point in working around it (you are creating a relationship
with the customer, he's not a one time visitor. Make him register).
(Kickstart itself doesn't allow anonymous checkout, uses commerce_checkout_redirect-like functionality to force login/register before checkout).
Comment #42
stewart.adam CreditAttribution: stewart.adam commentedPlease consider updating the documentation / project page with this fact just to avoid repeat bugs or RFE submissions.
Comment #43
bojanz CreditAttribution: bojanz commentedI've added this to the list of documentation that needs writing (currently we have 0 docs)
Comment #44
thermalmusic CreditAttribution: thermalmusic commentedHi,
I tried running the patch from #35 on the current commerce cardonfile dev version Aug 28, 2014 and get 1 Hunk fail:
patching file commerce_cardonfile.module
Hunk #1 succeeded at 514 (offset -41 lines).
Hunk #2 FAILED at 943.
Then its asking to recreate rules that exist, so not sure if I should proceed:
The next patch would create the file commerce_cardonfile.rules_defaults.inc,
which already exists! Assume -R? [n]
Is there an update for this patch?
I manage a site where anonymous users can join as paid members. They should be able to store a card on their initial purchase, before logging in for the first time. Otherwise, Recurring and Cardonfile seems to work fine barring this one hurdle.
Comment #45
mglamanDiscussed with bojanz. This is up for discussion again.
Comment #46
andyg5000I've worked through this in the past by creating a custom rules action to create the COF entity after the fact. The action can then be executed after the rule to create a new user account from anonymous order. The action uses the transaction remote id to re-query the payment gateway for transaction info and payment token. This may not always be an option, so we could store the info in the payload/data.
Comment #47
mglamanComment #48
drupov CreditAttribution: drupov commentedWell the patch from #35 applies cleanly against commit 075dc82 on 7.x-2.x (from April 16, 2014 17:00) - see the date of the patch. There are 36 commits after that one, so a lot has changed since.
I re-rolled the patch against the latest commit in dev (commit 1579916 on 7.x-2.x from July 23, 2015 16:08) and now it works.
Please review!
Comment #49
MichaelSt CreditAttribution: MichaelSt commentedI need this functionality on my site but I'm not comfortable with coding. If I apply the patch in #48 should I be good to go? Or do I need all the ones before that one.
Comment #50
drupov CreditAttribution: drupov commentedMichaelSt you need only the patch from #48, applied against the latest dev of the module
Cheers
Comment #51
AsadKamil CreditAttribution: AsadKamil at Valuebound commentedPatch applied successfully.
Thanks
Comment #52
MichaelSt CreditAttribution: MichaelSt commentedI just made the changes and tested on our live site - it seems to work as intended.
Comment #53
gravit CreditAttribution: gravit commentedAfter implementing patch #48 -
I have a build (with commerce_stripe) that allows anonymous users to see old/stale card radio buttons in checkout and use "existing cards on file" from previous orders that obviously were not theirs. This is obviously a very bad bug.
I am just starting to debug it - but one issue I see is that all uses of "commerce_cardonfile_load_multiple_by_uid" don't have a behavior set for ignoring UID == 0. This may be a good fail safe to implement.
Comment #54
companyguy CreditAttribution: companyguy commentedgravit , I'm seeing that as well in my build. Did you figure out a patch to fix that? This is a pretty major problem if it's doing what I think it's doing...
Comment #55
gravit CreditAttribution: gravit commentedHey Jason -
My current solution is an edit to the function responsible for loading card data for a user. I've modified it to load no data if you are not logged in. This assumes the proper workflow that if you are not logged in, no existing card data should be available.
Note that I believe I "uncovered" the issue because I was having some instances of checkout failing to correctly assign orders to users after the order completed. The use cases where checkout would "succeed" but rules would fail to finish the process of assinging the order to a new user ID, then the card would be saved in the database with an anonymous UID.
Module Devs: An equally important fix would probably be to make sure that the function that saves the card data removes that entry from the database if it can't find a uid to assign it to. I manually cleared cards out of my db that had a UID of 0.
Comment #56
mglamanBased on #53 this needs work.
Comment #57
mglamanAlso, this isn't a bug, it's a feature request :)
Comment #58
torgosPizza@gravit: IMO you should submit that patch to the module as a bug report. That seems rather major to me!
Comment #59
Kingdutch- Applied the patch to the latest version of dev
- Implemented fix as suggested by gravit in #55
Comment #60
Anas_maw CreditAttribution: Anas_maw at Vardot commented+1 for #59
Comment #61
deggertsen CreditAttribution: deggertsen as a volunteer commentedYes please to #59.
Comment #62
torgosPizzaIf the patch is working for people, you might consider setting the status to RTBC :)
Comment #63
Anas_maw CreditAttribution: Anas_maw at Vardot commentedThis should be set to RTCB
Comment #64
mirom CreditAttribution: mirom at Cheppers commentedWe are experiencing problem, that if user was created by anonymous user and then assigned to existing account, user had multiple default cards. Attached patch is solving this problem.
Comment #65
deggertsen CreditAttribution: deggertsen as a volunteer commentedGood catch mirom. I think your change is good so I'm setting back to RTBC.
Comment #67
mglamanThank you everyone! I went and credited all who participated, because I'm sure there was lots of testing on your own ends when integrating. WOOT!
Comment #68
attisanis it only me, or did this property implementation not make it into the entity information, hence commerce_cardonfile_entity_property_info nor the entity class CommerceCardOnFile?
would be great in order to be able to use entity_metadata_wrapper.
Comment #69
mglamanattisan, not sure which property. Can you open a follow up issue.
Comment #70
attisanI have attached a patch reflecting (fixing) the missing order_id.
hth
attisan
Comment #71
ibuildit CreditAttribution: ibuildit commentedThe latest dev does not work with the allow anonymous user patch, which appears to be needed.
The checkout crashes.... how ever I can highly recommend to use:
ajax_register
email_registration
Then you get a very smooth and slick registration form, just add email, you get logged in, redirected to where you want (i.e. checkout with a product already added via rules, or product offer page, and from there start a subscription. Then card on file will work just fine! :)
I'm running beta5 with patch in #23. Works GREAT. (only combo that appears to work with Recurring, Card on File and Braintree, at the moment)
Comment #72
deardagny CreditAttribution: deardagny commentedThe patch in #59 combined with #70 worked for me on latest dev.
@ibuildit – Checkout crashed for me at first. Then I realized I needed to run update.php in order to create the new order_id column included in the patch.
Seems to be working fine for me with one minor exception. Using Stripe, the purchaser's email doesn't make it to the merchant correctly on order creation. See this screenshot – the bottom order is before the patches, the top one is after. Not sure if this is a Stripe API issue or something that will be a problem across merchants. Looking into it now.