I bought my product 4 times in a diff browser w/ browser cache cleared. After Every order the logs show that I am "Receiving IPN at URL for order" and "IPN transaction verified." (note: it shows both of those twice... don't know why)

The first 2 times I bought the product, the order status is set to "payment received" The third time it sets the order status to "complete". The 4th order is set to "payment received".

No role is granted if order doesn't make it to "complete". Now I know I can just change the conditional actions so that role is granted on payment received... but they it wont grant it on that random "complete"....

Why Oh Why is it doing this? How can I debug this? I have probably made 30 some orders... and it is very random as to which status it decides to pick.

Using:
A product w/ 3 features: assign role, restrict quantity, recurring
drupal 6
ubercart 2.2
uc_recurring-dev as of today 6-23
Using WPS sandbox.
creating user account on checkout

Comments

that0n3guy’s picture

StatusFileSize
new36.29 KB
new31.56 KB

Also,

I don't have nice order status logs like the images here: http://drupal.org/node/580226 (i have "Enable order logging" turned on under admin/store/settings/orders/edit )

Mine look like so: http://drupal.org/files/order109_paymentrecieved.jpeg

I've also noticed that the time's at which things happen on the orders that are "completed" vs "payment received" are backwards.

For example:
- order 108 ( http://drupal.org/files/order108_completed.jpeg ) has a status of "completed", it has the paypal ipn reported -> customer granted user role -> then order created. This seems kinda backwards to me...

vs

- order 109 ( http://drupal.org/files/order109_paymentrecieved.jpeg ) has status of payment recieved, has teh order created THEN paypal ipn reported..

I checked some 5 or 6 different orders to compare and they follow the same pattern... "complete" orders are backwards from "payment receiveds"...

that0n3guy’s picture

Ok, I think this has to do with "time". By that I mean that the ipn needs to be reported before the order is fully created.

So if I go through the order process and after I order (at paypal) and wait the 5 second wait (if I have auto return in paypal turned on) or just wait before clicking the "return to store website" (or whatever its called) button for about 5 seconds. Then my order is set to "completed"

If I am impatient (like many people buying stuff) and click the "return to store website" button to soon. I don't ever make it to the "completed" state.

Seems very buggy to me....

univate’s picture

I hate working with paypal because the ability to test the gateway is such a pain in the *** - that is why I don't use this gateway myself. So I can only really help with committing patches to fix any issues people find.

By the sounds of things its working correctly if everything happens in the correct order, ie: if all the messages coming from Paypal are in the correct order it works, correct? Its just when things happen out of the order the logic in the Paypal gateway code is not equipped to handle that case.

that0n3guy’s picture

Component: Code » Gateway Integration

Yeah, thats correct. I would think this would be a fairly easy fix. I spent to much time on this the last couple of days so I won't be looking at the code much more (this is my first-ish ubercart project... learning-curves 'R us). It "works" for now (as long as the internet isn't slow and someone waits the 5 seconds) so I'm going to leave it be for the moment.

I'll be very happy to test some patches if anyone else is interested.

univate’s picture

Title: Order status inconsistent, sometimes set to "complete" sometimes "payment received" » Paypal WPS: Order status inconsistent, sometimes set to "complete" sometimes "payment received"
Issue tags: +Paypal WPS, +paypal
that0n3guy’s picture

So just to make sure this is a bug and not something I screwed up I retested on a super clean install.

Tested role adding w/ out uc_recurring... works like a charm
Added recurring feature and tada... no long works perfect. It never gets to "completed" if you come back to the site quicker than 5 seconds-ish.

So really, nothing new to add, just that it is uc_recurring and not ubercart that has the bug.

torgospizza’s picture

Subscribing.

Using latest dev of uc_recurring, when a Paypal WPS payment is completed, and an IPN notification is received, the order status is changed from In Checkout to Pending, despite the fact that I have Conditional Actions configured to change the status to Completed upon full payment+order status update.

I'll be digging into this more.

univate’s picture

If you are keen on fixing paypal, lets try and fix it at its source, as the moment uc_recurring has to add in all this duplicate code because ubercart does not allow us to get access to its IPN messages.

#781720: Refactor the Paypal WPS IPN code

univate’s picture

As for fixing this issue... we (ubercart & uc_recurring) do log the IPN messages to the database so if we get a subscription created message after the payment received messages, maybe we can look for an existing message in the db that says payment has been received and then process it as well so we don't end up in this state.

torgospizza’s picture

@Univate: Thanks! Will take a look at the patch in your linked Issue. Hopefully that should solve that particular issue. Would love to help test.

univate’s picture

That patch wont solve this issue, its just a step towards making uc_paypal more extensible so modules like this don't have to duplicate effort. The problem in this issues seems to be messages being received from paypal out of order and what I mentioned in #9 is probably what will need to be done to solve this issue.

that0n3guy’s picture

Just a question, if someone were to just site down and crank this out, about how long would you think it would take to get this fixed? I'm willing to front some $ and am kind trying to guess the price.

puravida’s picture

This will sound obvious, but: "did you check the order a few minutes later?"

In my own custom cart, I've often times seen paypal takes minutes to update the IPN, in which case things are still technically pending. Eventually it has always gotten updated, though. From what I read in this thread, though, that may not be the issue since it does work correctly if uc_recurring is not in the loop. Hmm.

Cheers,

Brandon

that0n3guy’s picture

Brandon,

Yeah I tried it, I setup several test sites w/ recurring orders going. I would let them run for weeks and randomly make orders. This is a very reproducible bug, I can get it to do it everytime.

univate’s picture

I think its worth mentioning that Paypal WPS no longer works with ubercart-2.3: #781010: Patch to Ubercart might break recurring Paypal WPS

torgospizza’s picture

To follow up on this, I have PayPal WPS still working with Ubercart 2.3 (have not upgraded yet) but, it seems that in this case an IPN is sent to the site twice; the first time, it contains a "payment_status" flag. The second time, it doesn't. This causes the order to go from "In Checkout to Completed" and then "Completed to PayPal Pending".

I could fix this with another Conditional Action, but that seems like more of a band-aid. I didn't see anything for a Default case in _uc_recurring_hosted_paypal_ipn_payment() (in uc_recurring_hosted/uc_recurring_hosted.paypal_ipn.inc) that might cause this, other than the fact that it doesn't know what to do if the $ipn->payment_status doesn't exist.

Any thoughts?

that0n3guy’s picture

targos,

I looked threw my ipn logs also, are you sure thats what is happening? My first ip in the logs is the one w/o the payment_status, and the second is the with it. So in my case it should be setting it to completed on the second ipn... but this doesn't always happen.

I did a test, I ordered a product and clicked back to my site right away making it not work right (it never gets to completed).

Then I ordered and waited the 4 or 5 seconds.. this order got to completed and works just fine. Below are the logs for the 2 trials.

Also, I didnt think uc_recurring was supposed to work with ubercart 2.3 or higher b/c of http://drupal.org/node/781010 , is that not true?

Here are the non working logs:
ipn1:

Receiving IPN at URL for order 319. <pre>Array (
[txn_type] => subscr_signup 
[subscr_id] => I-HD54SU1US8E0 
[last_name] => User 
[residence_country] => US 
[mc_currency] => USD 
[item_name] => Order 319 at BlahBlah
[business] => psell_1270666983_biz@BlahBlah.com 
[amount3] => 47.00 
[recurring] => 1 
[verify_sign] => Ax98fGNvu4iJ59L0TK.rDdV4.ZPBlahBlahEk8KDk3EoUk0FsFdgW
[payer_status] => verified 
[test_ipn] => 1 
[payer_email] => petero_1270666850_per@BlahBlah.com 
[first_name] => Test
[receiver_email] => psell_1270666983_biz@BlahBlah.com 
[payer_id] => BlahBlah
[invoice] => 319-1281567696 
[reattempt] => 1 
[subscr_date] => 16:02:10 Aug 11, 2010 PDT
[charset] => windows-1252 
[notify_version] => 3.0 
[period3] => 1 D 
[mc_amount3] => 47.00 ) </pre>

ipn2:

Receiving IPN at URL for order 319. <pre>Array ( 
[transaction_subject] => 
[payment_date] => 16:02:13 Aug 11, 2010 PDT 
[txn_type] => subscr_payment 
[subscr_id] => I-HD54SU1US8E0 
[last_name] => User 
[residence_country] => US
[item_name] => Order 319 at BlahBlah 
[payment_gross] => 47.00 
[mc_currency] => USD 
[business] => psell_1270666983_biz@BlahBlah.com
[payment_type] => instant 
[protection_eligibility] => Ineligible 
[verify_sign] => AiYECWn4HDOrtuTMiLBf4ZBlahBlahg41Bf2HDde4dZ8fZDeC8cYGp 
[payer_status] => verified 
[test_ipn] => 1 
[payer_email] => petero_1270666850_per@BlahBlah.com
[txn_id] => 6ME49141LA371164M 
[receiver_email] => psell_1270666983_biz@BlahBlah.com
[first_name] => Test 
[invoice] => 319-1281567696
[payer_id] => BlahBlah 
[receiver_id] => BlahBlah 
[payment_status] => Completed 
[payment_fee] => 1.66 
[mc_fee] => 1.66 
[mc_gross] => 47.00 
[charset] => windows-1252
[notify_version] => 3.0 ) </pre>

Here are the working logs:

ipn1

Receiving IPN at URL for order 320. <pre>Array ( 
[txn_type] => subscr_signup 
[subscr_id] => I-KTC1F6BTWVGD 
[last_name] => User 
[residence_country] => US 
[mc_currency] => USD 
[item_name] => Order 320 at IM for BlahBlah 
[business] => psell_1270666983_biz@BlahBlah.com 
[amount3] => 47.00 
[recurring] => 1 
[verify_sign] => AEVEu15Q2Fd.aoeYCsShcYGvmn-wAOY.BlahBlahHAg4qHnnbgfm7nre 
[payer_status] => verified 
[test_ipn] => 1 
[payer_email] => petero_1270666850_per@BlahBlah.com 
[first_name] => Test 
[receiver_email] => psell_1270666983_biz@BlahBlah.com 
[payer_id] => BlahBlah 
[invoice] => 320-1281569078 
[reattempt] => 1 
[subscr_date] => 16:24:54 Aug 11, 2010 PDT 
[charset] => windows-1252 
[notify_version] => 3.0 
[period3] => 1 D 
[mc_amount3] => 47.00 ) </pre>

ipn2:

Receiving IPN at URL for order 320. <pre>Array ( 
[transaction_subject] => 
[payment_date] => 16:24:57 Aug 11, 2010 PDT 
[txn_type] => subscr_payment 
[subscr_id] => I-KTC1F6BTWVGD 
[last_name] => User 
[residence_country] => US 
[item_name] => Order 320 at IM for HBlahBlah 
[payment_gross] => 47.00 
[mc_currency] => USD 
[business] => psell_1270666983_biz@BlahBlah.com 
[payment_type] => instant 
[protection_eligibility] => Ineligible 
[verify_sign] => A2LnQlyacUCLwsLv3rNChkL-l.BlahBlahCcZAYlo0.N8GmiLZbtRt 
[payer_status] => verified 
[test_ipn] => 1 
[payer_email] => petero_1270666850_per@BlahBlah.com 
[txn_id] => BlahBlah 
[receiver_email] => psell_1270666983_biz@BlahBlah.com 
[first_name] => Test 
[invoice] => 320-1281569078 
[payer_id] => BlahBlah 
[receiver_id] => BlahBlah 
[payment_status] => Completed 
[payment_fee] => 1.66 
[mc_fee] => 1.66 
[mc_gross] => 47.00 
[charset] => windows-1252 
[notify_version] => 3.0 ) </pre>
torgospizza’s picture

#17: Yours look right, but I'm also assuming that ipn2 comes in after ipn1? In my case, ipn1 (the notification from PayPal that is of txn_type "subscr_signup". Since this, in my case, doesn't include a "payment_status" element, that logic is failing, and so the payment status is being set to Paypal Pending.

I added a bit of logic to the code:

if ($ipn->payment_status) {
...
}

So that it only switches for payment_status if a payment_status exists.

I tested this on a live account just today, and it worked perfectly. I got both txn IPNs logged in for the order, the recurring fee (and role) has been setup, and everything else seems to be fine. The condition I'm checking for shouldn't affect anything other than setting the order status, and it was doing so by checking payment_status anyway - so it shouldn't have any side effects, but if it does let me know.

For me this solved my issue though I'm not sure it's the same as the OP.

But #13 since you're the OP I'll mention that I don't think your issue is necessarily uc_recurring's fault. Did you double check your Conditional Actions? There might be another module that is hooking into uc_recurring that's affecting the order status. In my case I'm using uc_recurring and uc_roles to add a "subscriber" role to a user's account, along with User points. The only place I have seen it act inconsistent in updating order status is as I've described above - and for my case it involves the fact that the order statuses are hard-coded into the uc_recurring module.

I'd suggest looking at your CA configs and other contrib modules and see if any of them could be overriding the status. Can you post the actual "logs" from your order? The ones that say "Order status changed from foo to bar"?

that0n3guy’s picture

ahhh... I see what your saying now. I didn't realize that their was a log section here: admin/store/orders/{order#}/log

Mine shows ipn1 show up before ipn2 (which seems like it should work right that way) but it does say "Order status changed from Pending to Payment received." and then right after says "Order status changed from Payment received to PayPal pending."

If I'm looking at the right code... its weird that the ipn1 would make it to the _uc_recurring_hosted_paypal_ipn_payment function if since txn_type doesn't equal subscr_payment.

Are you adding your "if" to function _uc_recurring_hosted_paypal_ipn_payment?

You mind posting a patch?

torgospizza’s picture

Yeah, I just add that condition to the beginning of that function, just before the switch($ipn->payment_status) line. I need to check my current code and see if using the patch from #781720-1: Refactor the Paypal WPS IPN code impacts any of this stuff. Not sure if you gave that a shot yet but it seems to move that function around.

Soon as I can roll a patch I will, but it's only a couple of lines. :)

bpirkle’s picture

I'm having similar issues to those described in this thread. I've turned on detailed logging and will report any significant findings. Did anyone ever come up with a solid solution for this issue? I tried the suggestion in #18 but that appeared to have no effect. As the switch doesn't have a default case, I'm not sure what the intention of that change was. But that suggestion was from a few months ago, so maybe that change was made on a different code base. I'm on 6.x-2.0-alpha5

Thanks!

bpirkle’s picture

Worth noting that I've seen orders stuck in both "Payment Received" and "PayPal Pending".

I'm curious whether there is a race condition within the "uc_recurring_hosted_paypal_ipn" function. I see watchdog entries like this in the log (oldest to newest):

Receiving recurring IPN at URL for order 4433.
Receiving recurring IPN at URL for order 4433.
PayPal Recurring IPN Transaction Verified.
PayPal Recurring IPN Transaction Verified.

I'm not terribly familiar with this code (yet), but I would have expected to see this:

Receiving recurring IPN at URL for order 4433.
PayPal Recurring IPN Transaction Verified.
Receiving recurring IPN at URL for order 4433.
PayPal Recurring IPN Transaction Verified.

I'm assuming that "uc_recurring_hosted_paypal_ipn" is triggered by the external notification from PayPal, and that if the two notifications arrive sufficiently close together, it is possible that it is called twice at the same time, once for each notification. This would become more likely if there are any delays due to database congestion, server load, etc. that delay the execution of the first invocation.

Does that sound like a possibiity to people more familiar with how all this works?

univate’s picture

Would be interested to find out if adding a lock/unlock on the paypal db table at the start/end of that IPN function would help, something like:

db_lock_table('uc_payment_paypal_ipn');
...
db_unlock_tables();
bpirkle’s picture

Thanks, univate.

My first attempt using your code failed - the order stuck at Pending with no ipn activity logged. This is only my second experience with table locking, so I could be way off base. But per http://dev.mysql.com/doc/refman/5.1/en/lock-tables.html, a session that locks tables can access only its locked while the lock is held. And as multiple tables are accessed within that function, your suggestion might not work as intended.

That aside, I'm very interested in your idea. I have memcache available, so I tossed a memcache lock around that function's body to see what would happen. My test transaction went okay, but I've had sporadic success reproducing the problem myself. So I'll run with the memcache lock awhile, see what happens, and report back. I realize this isn't a general solution, but at this point I'd be happy just to learn something.

bpirkle’s picture

StatusFileSize
new11.39 KB

I've processed enough orders to report back initial results. As expected, adding the lock resolved the race condition. So in the log, I now see:

Receiving recurring IPN at URL for order 4433.
PayPal Recurring IPN Transaction Verified.
Receiving recurring IPN at URL for order 4433.
PayPal Recurring IPN Transaction Verified.

Instead of:

Receiving recurring IPN at URL for order 4433.
Receiving recurring IPN at URL for order 4433.
PayPal Recurring IPN Transaction Verified.
PayPal Recurring IPN Transaction Verified.

That's a good thing, and a necessary first condition to fixing the stuck transactions. However, I am still seeing some orders get stuck while others go through fine. Presumably, this is due to the timing issues discussed by others earlier in this thread, but I haven't had a chance to really examine it yet. At the very least, all of my "stuck" orders have looked the same, whereas before there were two or three different (wrong) ways they could look. Consistent is good - much easier to debug.

To implement the lock, I had to change a few things in the "uc_recurring_hosted_paypal_ipn" function. That's because it previously could return directly from the switch statement, and that would have bypassed releasing the lock. I encapsulated the actual locking in two new functions:
uc_recurring_hosted_get_lock
uc_recurring_hosted_release_lock

There remains the issue of how to properly implement a lock. I used memcache, because I was already using it for something else and it was quick and easy. But we can't depend on people having it. I found mention of locking here:
http://api.drupal.org/api/drupal/includes--lock.inc/group/lock
Would this be appropriate?

I've attached a patch for what I'm currently doing, in case it helps somebody. To try different locking strategies, you should just have to modify the bodies of my two new (and very short) functions. Be sure to apply the patch offline and tweak for your server setup before trying to actually execute anything.

--Begin Disclaimer--
WARNING! DANGER! CAUTION! DO NOT ACTUALLY APPLY THIS PATCH ON A LIVE SERVER! IT DEPENDS ON CODE THAT YOU WON'T HAVE! IT WILL BREAK! USING THIS PATCH IS MORE DANGEROUS THAN RUNNING WITH SCISSORS! EVEN THE REALLY SHARP POINTY ONES, NOT JUST THE DULL ROUNDED ONES LIKE WE USED IN KINDERGARTEN!
--End Disclaimer--

bpirkle’s picture

StatusFileSize
new11.92 KB

I realized I didn't put in a sleep-retry loop in my memcache lock (duh) so that was doomed to failure. I've corrected it now. Orders have slowed down for the moment, but I'll be watching to see if I still have stuck orders with this correction.

I also tried the drupal lock_acquire/lock_wait/lock_release functions, but found they weren't available on my Drupal installation. A quick Google revealed this to be a known issue, so I went back to memcache for now.

The attached patch has a now-hopefully-correct memcache lock, plus commented-out drupal locking, in case there is anyone who whould like to give that a try and whose installation is happy with those functions.

Again, unless you change the code yourself, the patch depends on my custom memcache code, and IS NOT USABLE ON YOUR SITE AS-IS.

univate’s picture

Was not even aware of those lock functions. That looks to be exactly what is needed here.

If the issue you are referring to is #732064: "Call to undefined function lock_acquire", locale module, that appears to be fixed in the current 6.x release.

bpirkle’s picture

I'm still seeing stuck orders, even with the locking. I can't see anything different in the ordering or content of the PayPal IPN messages in the detail logs for working vs. nonworking orders.

How does uc_recurring fit into the Ubercart's overall PayPal processing system (with which I am completely unfamiliar). My lock prevents simultaneous execution of just the "uc_recurring_hosted_paypal_ipn" function. Is it possible that other Ubercart code is having race condition problems outside the context of my lock, resulting in the issue?

univate’s picture

uc_recurring WPS code overrides the ubercart paypal code, it isn't interfering here.

Personally I would like to reduce the need to duplicate code and re-use the core ubercart paypal code, but no-one else seems interested in allowing this: #781720: Refactor the Paypal WPS IPN code

puravida’s picture

@univate
I think you are right to want to use the ubercart paypal code but it sounds like that code isn't very well written and that team is unwilling to improve it in a timely manner. So others here are leaning on uc_recurring to do it (not really ideal). For us, the WPS code in uc_recurring isn't acceptable because it only supports a single recurring item but still allows for checkout. That was a real pain to fix each order that came in and we quickly disabled WPS support.

I see only two ways that WPS support can work in ubercart (based on feedback from our current cart developer). The WPS API is intentionally designed to support only 1 recurring order per checkout. So, either:

1. We would use "reference transactions" (http://drupal.org/node/854536) to handle multiple recurring per order. However, it is possible that "reference transactions" are only supported by WPP.

or

2. We would allow an option to block users with a custom message and redirect back to the cart. Users would then be forced to remove all but 1 recurring item, if they then select Paypal payment method.

We are currently funding the development of #2 in the hopes of finally getting rid of our old cart solution but that is far less than ideal of a solution.

bpirkle’s picture

I remain completely baffled on why some orders are getting stuck.

I added more detailed logging, including to-the-second timestamps. I just compared a stuck order to a successful one, and found absolutely no difference in the order or content of the IPN messages. (Things like customer names and order numbers differed, but those are expected to change.) The timestamps indicate that the locking is doing its job and the IPNs are now being processed sequentially rather than concurrently, and in the same order between the stuck and successful order.

What else in the code might be subject to race/timing issues or nondeterministic ordering? Are there any identical module or action weights that might be competing for which executes first for different orders? I know I'm reaching here, but *something* is up.

bpirkle’s picture

Ok, finally observed something that may be useful. This goes all the way back to comments #1 and #2 in this thread, but I was either too dense or too confused to recognize it at first.

For good orders, if I pull up the order detail page, the last admin comment is "Order created through website". For stuck orders, that comment either does not appear, or appears before the IPN comment about "Payment reported".

Near as I can tell, that comment is created only by Ubercart code, not uc_recurring code, in the section that ends up with an order getting set to Completed. So there appears to be a race condition between PayPal's IPNs (and the related uc_recurring code) and whatever is triggering Ubercart to add that comment. Does that make sense, and any suggestions what to do about it?

I'm getting pretty worn out on this issue, and I'm getting really tempted to just hack in something. My first thought is a call to function uc_cart_complete_sale (file uc_cart.module) from _uc_recurring_hosted_paypal_ipn_payment (file uc_recurring_hosted.paypal_ipn.inc).

I fully appreciate that cleaning up Ubercart's PayPal processing is the better overall solution. But I'm tired of getting hatemail from customers, so I really need to get fix (even a hackish one) working soon. After that, I'm more than willing to help test more comprehensive patches on our site. (We have a development site where I could try out some speculative things separate from live code.)

bpirkle’s picture

I made some changes to both Ubercart and uc_recurring code, and have now been running for a few days, and a number of orders, with nothing getting stuck. The final issue was that the original code had a three-way race condition:

1) the "subscr_payment" IPN, handled by uc_recurring
2) the "subscr_signup" IPN, handled by uc_recurring
3) the Ubercart "cart/checkout/complete" menu item, which results in calls to uc_cart_checkout_complete and uc_cart_complete_sale

My previous locking efforts addressed #1 and #2, but not #3. Addressing that required three things:

1) make subscr_signup not bump to PayPal Pending if status is already Completed
2) bump to Completed upon subscr_payment
3) don't invoke hooks and triggers upon cart/checkout/complete if status is already Completed

The one remaining thing that I might should do is to add a lock in uc_cart.pages.inc to prevent simultaneous execution of #3 with #1 or #2. But this hasn't been a problem thus far. If it is, the same locking functions could be employed (and in fact must, so that the lock is shared by all three events).

I don't pretend to understand everything in Ubercart, and my rather hackish fix probably fails for all sorts of other store configurations besides mine. And (as mentioned in comment #25) my lock depends on my own custom code, so the attached patches WILL BREAK ANYONE ELSE'S STORE UNLESS THEY CHANGE TO THEIR OWN LOCKING MECHANISM!!!!

Still, it is working for me at the moment, so I'm happier than I was a week ago.

I'm still willing to try out more comprehensive Ubercart/PayPal integration changes, if someone will tell me what I can do to be helpful.

torgospizza’s picture

Awesome work. I think if these race conditions are verifiable and easily reproduced, then we should definitely consider using lock_acquire() and lock_release() as demonstrated on that API page. Since it's a pluggable API you should be able to use it with something like memcache (though I haven't dug into it that far yet, that's just how the page reads).

Race conditions like this also lead to some CA weirdness, like if we have an order that contains shippable and non-shippable items, there are times when our "set order status to 'Pending Shipping' if order contains shippable items" Action fails, because of the digital downloads. In other words it appears that the order of operations changes due to the race condition, resulting in some anomalies.

+1 for this, thanks for your detailed investigation and hard work. I bet a lot of us contrib maintainers could benefit from this locking mechanism, especially when involving Ubercart.

Anticosti’s picture

Subscribe

that0n3guy’s picture

Status: Active » Needs review
StatusFileSize
new3.11 KB

Ok, I started looking into this. I think there are timing issue problems here. Please correct me if I'm wrong, but this is how I see it:

subscr_payment vs subscr_signup

The first problem is the race between subscr_payment and subscr_signup in the recurring ipn. If subscr_signup gets to the site before subscr_payment... all is good. If subscr_payment is there before subscr_signup, things go bad.

This where the "locking" talk above is directed... correct?

User vs IPN

The second problem is the race between the user coming back to the site, vs the IPN coming back the site. This is also an issue with the normal paypal ubercart module...probably because the paypal code needs revamped (as univate always says :P).

If user gets back before the recurring ipn, the order will never be marked as complete as the trigger "uc_checkout_complete" is never pulled by the ipn. Also, if the user never comes back to the site, the user is never created, the order never gets set to complete, and the order emails are never sent out to the email address. This issue really hasn't been talked about yet it in this thread.

My fixes

For subscr_payment vs subscr_signup: I added a sleep(20); Ha, hackish but it will probably work 90%+ of the time... here is my logic:
- Paypal probably sends signup ipn before payment ipn... it probably just gets here 2nd sometimes b/c of how its routed to the site...
- its just 20 seconds... who cares

for user vs ipn
I did this similar to the way I did the duplicate emails w/ normal paypal: http://drupal.org/node/644538#comment-3982972 . I talked about it more at #204 and #192 in that thread.

The attached patch seems to work pretty good. It needs to be used with this patch: http://drupal.org/node/644538#comment-3982972

... its simple, but it works.

EDIT: Oh, and this is against current uc_recurring 6.x-2.x-dev
Edit x2: I haven't tested this with the whole "recurring" part yet... will find out tomorrow when my recurring stuff goes through.

torgospizza’s picture

Thanks for your hard work, will test this when I can.

univate’s picture

Status: Needs review » Needs work

The subscr_payment IPN is the most common IPN you will receive as it gets sent on each renewal, so I don't think putting a sleep here is a good idea, which will keep that apache/php process active (potential blocking other requests) for 20seconds.

We should at least check and only do this on the first payment - there shouldn't be a problem with recurring payments.

I am sure there is a better way to solve this problem without a sleep at all though.

kvoltz’s picture

Hey guys,

I think I am seeing a similar problem on one of my sites. I am using PayPal WPP which is working to a degree... but I cannot get roles to grant when the payment is received. I have pushed the conditional actions around repeatedly in an attempt to resolve this - I have set the role to fire when payment is received, I have set the order to fully complete when payment is received, I have gone through everything I can think of to make this work. The only way I can get the role to grant is if I go in and "complete" the order manually (it simply will not complete on its own).

Forgive my ignorance, but is this an extrapolation of the same issue?

-kirk

Update: It looks like my problem was related to a hang up with another module. Appologies for thread-crashing.

that0n3guy’s picture

@univate - I agree. I will definitely put my sleep in so it only gets run on first payment.

I also agree that there has to be a better way to handle this, but I've messed with it for a couple hours today and can't come up with anything good. I don't have any more time to deal with this so I'm going to stick w/ the sleep for now.

What I tired:
- set a flag so when subscr_signup runs, it stores in the db that it has been run.
- when case subscr_payment is run, check to see if signup has been run, if not, store the ipn in the DB and end.
- after case subscr_signup runs, check if payment ipn is stored, if so, re-run uc_recurring_hosted_paypal_ipn with the stored ipn.

This doesn't work because signup could start running and before its finished payment could run and get done before signup. It was a lame attempt on my part I think :).

But... why do we even need to use the subscr_signup. We don't actually use any data from it. We basically just use it as a trigger to run uc_recurring_product_process_order. Why cant we just fire uc_recurring_product_process_order the first time we get the payment?

that0n3guy’s picture

I thought I would throw out a quick update on this. I may have found a simple fix but am still testing so no patch for right now, but here are the details:

- when we get the subscr_signup ipn, we are running uc_recurring_product_process_order()... that is basically all that subscr_signup ipn is doing.... so why not just have subscr_payment ipn do this?

So what I did was a couple simple modifications:

Original dev code in uc_recurring_hosted.paypal_ipn.inc

39 case 'subscr_signup':
40 case 'recurring_payment_profile_created':
41 // First we need to create the recurring fee as paypal_wps overrides the
42 // submit order function where this normally happens.
43 uc_recurring_product_process_order($order);
44 // Subscriptions do not have a txn_id, so we will use the subscr_id.
45 $ipn->txn_id = $ipn->subscr_id;
46 break;
47 case 'subscr_payment':
48 case 'recurring_payment':
49 // If the order already has been paid for then this must be a renewal.
50 if (uc_payment_balance($order) <= 0) {
51 // Fetch fee from database.
52 $fees = uc_recurring_get_fees($order);
53 foreach ($fees as $fee) {
54 $fee->ipn = $ipn;
55 uc_recurring_renew($fee);
56 }
57 }
58 else {
59 // Process the first subscription payment.
60 _uc_recurring_hosted_paypal_ipn_payment($ipn);
61 }
62 break;

Made these changes

      case 'subscr_signup':
        // Subscriptions do not have a txn_id, so we will use the subscr_id.
        $ipn->txn_id = $ipn->subscr_id;

      break;
      case 'recurring_payment_profile_created':
	  uc_recurring_product_process_order($order);

        // Subscriptions do not have a txn_id, so we will use the subscr_id.
        $ipn->txn_id = $ipn->subscr_id;

        break;
      case 'subscr_payment':
      case 'recurring_payment':
        // If the order already has been paid for then this must be a renewal.
        if (uc_payment_balance($order) <= 0) {
          // Fetch fee from database.
          $fees = uc_recurring_get_fees($order);
          foreach ($fees as $fee) {
            $fee->ipn = $ipn;
            uc_recurring_renew($fee);
          }
        }
        else {
          // Process the first subscription payment.
          // re-enable this for first order uc_recurring_product_process_order($order);
	  if ($order->payment_method == 'paypal_wps') {
	    //moved from case subscr_signup...STS mod
	    // First we need to create the recurring fee as paypal_wps overrides the
	    // submit order function where this normally happens.
	    _uc_recurring_hosted_paypal_ipn_payment($ipn, $order);
	  }
        }

        break;

You can see I duplicated $ipn->txn_id = $ipn->subscr_id; into case 'subscr_signup': and added a break. Then in case 'subscr_payment': I added a check for paypal wps and run uc_recurring_product_process_order().

Testing seems to be going well, but since 1 day is our smallest recurring interval... it takes time :).

Any ideas on how this might effect WPP? I figured case 'recurring_payment_profile_created': was a ipn from WPP so I left it alone.

gittosj’s picture

Issue tags: +Paypal WPP, +Sagepay

Interested to know whether anyone has made any progress on this. I'm still hitting the same issue: User is sometimes returned from Paypal WPS with the order state set as 'Payment Received' not complete and is not therefore given the correct permissions as a subscriber. I'm using:

Ubercart 6.x-2.x-dev
uc_recurring 6.x-2.x-dev
and a Paypal WPS account

The issue seems to be the same on both Sandbox and Live testing. My PHP skills and time are not enough to help fixing the issue but I'm now stuck. Is there some workaround we could use with altering the trigger / conditional action until wiser heads have been able to fix this?

Running a UK business and I seem to be slightly stuck with uc_recurring. Does anyone else in the UK have it working in production? Alternatives to Paypal WPS seem to be Paypal WPP - does anyone have this working with a recurring subscription? How about Sagepay?

gittosj’s picture

@univate - I'd love to sponsor a fix for this for production use - impressed with uc_recurring and would love to use it. Understand that WPS is a foobar to debug but outside the US, Paypal or Sagepay are the uc-recurring alternatives. Lot of users seem to feel that WPS is safer / quicker than entering credit-card details.

If sponsorship would help then let me know although realise you may be snowed with other issues....

Jeff Burnz’s picture

@that0n3guy - could you summarize what we need to do to make this all work with WPS - its a little confusing following all the posts in the thread - I am also more than happy to bounty this to get it working well.

mcpuddin’s picture

Status: Needs work » Fixed

I recently had this issue on 6.x-2.0-alpha6, however this issue seems to be resolved in 6.x-2.0-beta1 and in 6.x-2.x-dev ( explanation below ), as a result I'm marking it as "fixed" -- feel free to reopen if this is not the case

----------

In Summary, paypal IPN was sending two statuses back, subscr_signup and subscr_payment. If subscr_signup came second, the payment status would be marked from "Completed" to "Payment Received". @that0n3guy proposed two fixes, one in comment #36, which he then simplified down to comment #41. However, with some other fixes in the more recent versions, the changes in comment #41 are no longer needed because..

Comment #41 was proposed because in 6.x-2.0-alpha6, the flow was

  1. uc_recurring_hosted_paypal_ipn ( with txn_type = subscr_signup )
  2. uc_recurring_product_process_order
  3. uc_recurring_invoke
  4. uc_recurring_hosted_paypal_wps_process
  5. uc_order_update_status ( this would change from complete back to payment received )

However, in both 6.x-2.0-beta1 and 6.x-2.x-dev, uc_order_update_status was removed the flow is now

  1. uc_recurring_hosted_paypal_ipn ( with txn_type = subscr_signup )
  2. uc_recurring_product_process_order
  3. uc_recurring_invoke
  4. uc_recurring_hosted_paypal_wps_process

Status: Fixed » Closed (fixed)
Issue tags: -Paypal WPS, -paypal, -Paypal WPP, -Sagepay

Automatically closed -- issue fixed for 2 weeks with no activity.