OZtree icon indicating copy to clipboard operation
OZtree copied to clipboard

Page to try again if paypal fails to go through to payment

Open hyanwong opened this issue 3 years ago • 16 comments

On slack,. I noticed:

I went to hit “pay on paypal”; the PP page prompted me to log in , but I somehow got the PW wrong on the paypal login the first time, so it asked again and I got it right. It then asked to send a verification number by text, so I did that, got a whatsapp with a security code, typed that in, and it took me to my “normal” paypal dashboard page, without taking payment

If PayPal fails like this, @lentinj suggests that we put a "Ooops, that didn't work, can I try again" button on the views/default/sponsor_replace_page.html page. That seems a good idea to me, although we want to avoid having people accidentally paying twice.

hyanwong avatar Jan 24 '22 19:01 hyanwong

After discussion with Yan just now we've come up with the following...

This should be done on spl_waitpay.html: the page that loads if someone tries to sponsor a leaf that's already sponsored but not verified.

Case 1: sponsorship form submitted successfully in last ten minutes but PayPal not posted

"This leaf has been sponsored in the last 10 minutes. If this was you, we may not yet have received your payment. Come back and check in X minutes, and we expect the leaf to either be verified and active on the OneZoom tree, or be marked as "awaiting verification"."

Case 2: sponsorship form submitted successfully more than 10 minutes ago but PayPal not posted

"This leaf has been sponsored but we're still awaiting payment. If you've had trouble paying and your payment hasn't gone through then please [click on this link] to try again. If your payment has gone through and you have a receipt, but are still seeing this message then please [e-mail us] and we'll fix it for you."

NB: if the sponsorship form was submitted successfully and PayPal posted OK, then there is already logic to divert the user to spl_unverified.html

I think there should also be some text on sponsor_replace_page.html saying "if you have problems paying, go to this [spl_waitpay.html] page"

jrosindell avatar Jan 24 '22 22:01 jrosindell

Also, PayPal have a cancel_return option, to send users back somewhere if the payment process doesn't finish. This should point to the same location to allow users to try again.

lentinj avatar Jan 25 '22 10:01 lentinj

To make this work we need to know we're dealing with the right person (i.e. they have the form_reservation_code). Rather than invent a new state I'm thinking that "unverified waiting for payment" should imply waiting for payment from you, and otherwise you just get regular "unverified", and told it's sponsored.

lentinj avatar Jan 25 '22 11:01 lentinj

I don't think we do need to know that, actually. James and I thought that if someone wanted to pay for you, that would be fine. As long as they don't see your address, postcode, etc (which I guess would be taken from the DB and passed straight to PP without being present on the web page, if possible)

hyanwong avatar Jan 25 '22 13:01 hyanwong

Rats. That was the hard part of this :)

They'd know how much you paid (well, promised to pay), no other detail. We don't tell paypal anything other than OTT & price_paid. We could send the e-mail address, on the assumption that their PayPal account is probably the same, but even if we did that, it wouldn't be preserved when someone else goes to try and sponsor that OTT.

In this case I'm less convinced about the requirement to wait 10 mins before getting the paypal link again. If I want someone else to pay for me, I copy and paste the link but then they have to wait 10mins?

Should we decrease the timeout to 10 seconds? The PayPal IPNs generally come back faster than that IMO (around the time it takes to find the window MySQL is open in and re-run the query 3 times).

lentinj avatar Jan 25 '22 14:01 lentinj

Rats. That was the hard part of this :)

Yes, sorry. I missed your comment above this morning.

They'd know how much you paid (well, promised to pay), no other detail.

I think that's fine.

In this case I'm less convinced about the requirement to wait 10 mins before getting the paypal link again. If I want someone else to pay for me, I copy and paste the link but then they have to wait 10mins?

It's so that no-one pays twice. We want to give enough time that we are sure that the delay is just because PP hasn't got round to contacting us yet. 10 mins is maybe too much, though?

Should we decrease the timeout to 10 seconds? The PayPal IPNs generally come back faster than that IMO (around the time it takes to find the window MySQL is open in and re-run the query 3 times).

Maybe make it 30 secs or a minute? I have had them come back slower than 10 secs.

Also, what if we have the call from PP, but our call back to them is blocked (like the 503 errors I was getting). I guess in this case we don't want to bring up the "try again" page. So that does argue that we want to save e.g. the PP transaction ID when we get a call to pp_post_process, even if we can't verify it by communication back to PP for the moment?

hyanwong avatar Jan 25 '22 15:01 hyanwong

Also, what if we have the call from PP, but our call back to them is blocked (like the 503 errors I was getting). I guess in this case we don't want to bring up the "try again" page.

There's only so much we can second guess. It could be PayPal have frozen funds for PayPal reasons. It could be the user knows they mucked up for some reason. If we knew exactly which transactions were valid or not whilst they're still in-flight, we wouldn't consider them still in-flight anymore.

The user should have a reasonably clear idea whether they've paid or not, trying to second guess this with heuristics, stopping them paying when they know they need to is just going to annoy people IMO.

lentinj avatar Jan 25 '22 15:01 lentinj

True, but in the case where we have heard back from PP, we do know (more or less, unless it's some weird hack) that this is paid. So there's no reason to require them to pay again (I think).

hyanwong avatar Jan 25 '22 16:01 hyanwong

Okay, I think the above does what we need. The final commit still hands around form_reservation_code as if it's required for payment retries, of course it's not any more, but it's the fiddly bit to restoring repayments-only-for-sponsorer, and it stops some form_reservation_code churn.

The final commit also has the template changes if you want to have a look and rewrite.

lentinj avatar Jan 25 '22 16:01 lentinj

True, but in the case where we have heard back from PP

Either we officially heard back from PayPal, and the transaction is now verified, or we haven't. There is no useful "We heard something, most likely a hack attempt" transaction state.

If the IPN callback verification is failing, ether we've rolled out buggy code (and we need to notice and fix the error), or PayPal is on the blink and we need to wait for the retries. There's plenty of other points in the process PayPal can fall over---it may never send us IPNs at all---and we can't claim to handle these.

lentinj avatar Jan 25 '22 16:01 lentinj

I think you are right, but:

If the IPN callback verification is failing, ether we've rolled out buggy code (and we need to notice and fix the error), or PayPal is on the blink and we need to wait for the retries.

Yes, but then we shouldn't be soliciting a replacement payment from the user, I think. That's all I meant.

hyanwong avatar Jan 25 '22 16:01 hyanwong

James and I thought that if someone wanted to pay for you, that would be fine

@jrosindell and I had a chat and realised that this may not be so simple. If someone else pays for you, then we mix your and their details in the reservations table. That means they can use e.g. their PayPal email address to get hold of your personal private renewal page.

@lentinj: I'm not quite up to speed on what you did here. Is the machinery still in place to disallow other people paying for you (i.e. using the form_reservation_code)? Perhaps if we don't have a form_reservation_code we could require the user to type in the email that they used to sponsor before they pay, as a confirmation (and we could even send an email out to that address saying that they have tried to pay).

Given this complication, I haven't made this set of commits live on beta or prod yet, but the sponsorship pathway should work fine without them, so I think we can release the current OneZoom main branch as the final 3.6 now, can't we? And anything still in the 3.6.1 branch can go into a 3.6.1 release.

hyanwong avatar Jan 26 '22 10:01 hyanwong

Is the machinery still in place to disallow other people paying for you (i.e. using the form_reservation_code)?

No. sponsor_pay() carries around the previous value where it can, here for example, sponsor_leaf_check() will reuse the value instead of making a new one but it's of no real consequence.

The part that I removed was here:

https://github.com/OneZoom/OZtree/blob/ee4670492179add284851927c313039e4930902e/modules/sponsorship.py#L569-L572

There was an extra check that form_reservation_code matched user_registration_id, if it didn't then it would return just "unverified", hiding any of the retry links from other users (and the fact that the competing sponsor hadn't paid yet).

we could require the user to type in the email that they used to sponsor before they pay, as a confirmation

That'd be easy enough, with an extra state of "unverified missing form_reservation_code" instead of "unverified" above, which directs to another template to do the e-mail and send a sponsor_pay?ott=...&form_reservation_code=... link.

Before getting too carried away though, it might be worth thinking about how/if to translate this into basket_codes. Ideally after hitting donate we start dealing with basket_codes, not ott/form_reservation_code. This means we can get rid of a few paypal pathways.

lentinj avatar Jan 26 '22 10:01 lentinj

Good point about basket codes. So perhaps we can leave this set of commits for the moment and rework them to include basket codes & simplify the payments pathway during the next sprint in March?

hyanwong avatar Jan 26 '22 10:01 hyanwong

Depends how essential you think retries are really. I've no problem picking what's here apart to convert to basket_code. But going further with an e-mail workflow would be silly.

lentinj avatar Jan 26 '22 11:01 lentinj

I hope we shouldn't need them too much now we pop out on form submission. So happy to leave until March, when we can revise our judgement if need be.

hyanwong avatar Jan 26 '22 11:01 hyanwong