Hi!
I had a problem way back when returning from PayPal faster than the
IPN was received.
The site's user experience would clearly be less than stellar if the site
didn't know when the payment was received; the user would be returned,
he'd look around and see "pay up" links although he just did pay, and then
refresh the site and the encouragenemnts to pay would suddenly be gone.
What needed to be done is a way to keep the user on hold until the IPN
is received, and as far as I know, there is no way in django-paypal to do
this.
PDT seems like it MIGHT take care of this, but it's terribly complex,
so probably it's easiest to stick to IPNs only.
I wrote code to accomplish this.
There's a new variable called from_view in the model.
When PayPal sends an IPN, the from_view is set to 'notify'.
In our return URL we have the txn_id and we can use that and the from_view
to see if an IPN has come in from PayPal. If not, we set context variables
to refresh (using html meta refresh) the current page. If we got it, we
set context variables to refresh us to another page.
Some earlier discussion is at
http://groups.google.com/group/django-paypal/browse_thread/thread/0ebba9877e4bbbd3#
And the actual code at
http://github.com/mjtorn/django-paypal/tree/track_view
The code does not break any APIs, though it does change how the default
notify_url works. It introduces a wrapper called handle_ipn_post in there,
so we can IN OUR RETURN VIEW set the from_view variable to 'return'
Now I have couple of projects again that need PayPal, and I do not want
to code against my semi-unmaintained branch forever, but either:
1. Get this merged
2. Hear from you how you handle returning to the site before the IPN arrives
Thanks!
PS. Here's the real code:
return_ipn_check.html is just a simple template that accepts
meta_refresh and meta_refresh_url variables, and says "Awaiting reply from
PayPal, you will be redirected shortly. If you suspect a problem, contact
support with {{ ipn_ob.invoice }}" or something like that.
The return view code:
def pp_return(request, txn_id=None):
"""View that looks at IPN status based on return POST
"""
if not txn_id:
txn_id = request.POST['txn_id']
context = {
'meta_refresh': True,
}
try:
ipn_ob = pp_models.PayPalIPN.objects.get(txn_id=txn_id, from_view='notify', payment_status__in=(ST_PP_COMPLETED, ST_PP_DENIED))
if ipn_ob.payment_status == ST_PP_COMPLETED:
context['meta_refresh_url'] = reverse('webshop')
except pp_models.PayPalIPN.DoesNotExist:
try:
ipn_ob = pp_models.PayPalIPN.objects.get(txn_id=txn_id, from_view='return')
except pp_models.PayPalIPN.DoesNotExist:
ipn_ob = pp_views.handle_ipn_post(request, from_view='return')
ipn_ob.save()
order = models.Order.objects.get(invoice=ipn_ob.invoice)
order.ipn.add(ipn_ob)
context['meta_refresh_url'] = '%s' % (reverse('pp_return', args=(txn_id,)))
context['ipn_ob'] = ipn_ob
context['txn_id'] = txn_id
context.update(request.POST.copy())
req_ctx = RequestContext(request, context)
return render_to_response('return_ipn_check.html', req_ctx)
--
mjt