2013-11-07 18:04:44 +00:00
# -*- coding: utf-8 -*-
2013-11-27 16:02:18 +00:00
try :
import simplejson as json
except ImportError :
import json
2013-11-07 18:04:44 +00:00
import logging
2013-11-22 11:48:31 +00:00
import pprint
2016-12-19 16:34:06 +00:00
import urllib
2013-12-03 14:25:01 +00:00
import urllib2
2014-01-24 14:59:23 +00:00
import werkzeug
2013-12-03 14:25:01 +00:00
2014-01-29 17:15:20 +00:00
from openerp import http , SUPERUSER_ID
from openerp . http import request
2013-11-07 18:04:44 +00:00
_logger = logging . getLogger ( __name__ )
class PaypalController ( http . Controller ) :
_notify_url = ' /payment/paypal/ipn/ '
_return_url = ' /payment/paypal/dpn/ '
_cancel_url = ' /payment/paypal/cancel/ '
2013-12-04 16:07:38 +00:00
def _get_return_url ( self , * * post ) :
""" Extract the return URL from the data coming from paypal. """
return_url = post . pop ( ' return_url ' , ' ' )
if not return_url :
2016-10-28 11:21:04 +00:00
custom = json . loads ( post . pop ( ' custom ' , False ) or post . pop ( ' cm ' , False ) or ' {} ' )
2013-12-04 16:07:38 +00:00
return_url = custom . get ( ' return_url ' , ' / ' )
return return_url
2016-10-28 11:21:04 +00:00
def _parse_pdt_response ( self , response ) :
2016-12-19 16:34:06 +00:00
""" Parse a text response for a PDT verification .
2016-10-28 11:21:04 +00:00
: param response str : text response , structured in the following way :
STATUS \nkey1 = value1 \nkey2 = value2 . . . \n
: rtype tuple ( str , dict )
: return : tuple containing the STATUS str and the key / value pairs
parsed as a dict
"""
lines = filter ( None , response . split ( ' \n ' ) )
status = lines . pop ( 0 )
pdt_post = dict ( line . split ( ' = ' , 1 ) for line in lines )
2016-12-19 16:34:06 +00:00
# html unescape
for post in pdt_post :
pdt_post [ post ] = urllib . unquote_plus ( pdt_post [ post ] ) . decode ( ' utf8 ' )
2016-10-28 11:21:04 +00:00
return status , pdt_post
2013-11-22 11:48:31 +00:00
def paypal_validate_data ( self , * * post ) :
""" Paypal IPN: three steps validation to ensure data correctness
2013-11-07 18:04:44 +00:00
2013-11-22 11:48:31 +00:00
- step 1 : return an empty HTTP 200 response - > will be done at the end
by returning ' '
- step 2 : POST the complete , unaltered message back to Paypal ( preceded
2016-10-28 11:21:04 +00:00
by cmd = _notify - validate or _notify - synch for PDT ) , with same encoding
- step 3 : paypal send either VERIFIED or INVALID ( single word ) for IPN
or SUCCESS or FAIL ( + data ) for PDT
2013-11-22 11:48:31 +00:00
Once data is validated , process it . """
res = False
2013-12-03 14:25:01 +00:00
new_post = dict ( post , cmd = ' _notify-validate ' )
2014-08-07 09:41:18 +00:00
cr , uid , context = request . cr , request . uid , request . context
reference = post . get ( ' item_number ' )
tx = None
if reference :
tx_ids = request . registry [ ' payment.transaction ' ] . search ( cr , uid , [ ( ' reference ' , ' = ' , reference ) ] , context = context )
if tx_ids :
tx = request . registry [ ' payment.transaction ' ] . browse ( cr , uid , tx_ids [ 0 ] , context = context )
2016-10-28 11:21:04 +00:00
pdt_request = bool ( new_post . get ( ' amt ' ) ) # check for spefific pdt param
if pdt_request :
# this means we are in PDT instead of DPN like before
# fetch the PDT token
new_post [ ' at ' ] = request . registry [ ' ir.config_parameter ' ] . get_param ( cr , SUPERUSER_ID , ' payment_paypal.pdt_token ' )
new_post [ ' cmd ' ] = ' _notify-synch ' # command is different in PDT than IPN/DPN
2014-11-03 13:06:52 +00:00
paypal_urls = request . registry [ ' payment.acquirer ' ] . _get_paypal_urls ( cr , uid , tx and tx . acquirer_id and tx . acquirer_id . environment or ' prod ' , context = context )
2014-08-07 09:41:18 +00:00
validate_url = paypal_urls [ ' paypal_form_url ' ]
urequest = urllib2 . Request ( validate_url , werkzeug . url_encode ( new_post ) )
2013-12-03 14:25:01 +00:00
uopen = urllib2 . urlopen ( urequest )
resp = uopen . read ( )
2016-10-28 11:21:04 +00:00
if pdt_request :
resp , post = self . _parse_pdt_response ( resp )
if resp == ' VERIFIED ' or pdt_request and resp == ' SUCCESS ' :
2013-11-22 11:48:31 +00:00
_logger . info ( ' Paypal: validated data ' )
2014-08-07 09:54:30 +00:00
res = request . registry [ ' payment.transaction ' ] . form_feedback ( cr , SUPERUSER_ID , post , ' paypal ' , context = context )
2016-10-28 11:21:04 +00:00
elif resp == ' INVALID ' or pdt_request and resp == ' FAIL ' :
_logger . warning ( ' Paypal: answered INVALID/FAIL on data verification ' )
2013-11-07 18:04:44 +00:00
else :
2016-10-28 11:21:04 +00:00
_logger . warning ( ' Paypal: unrecognized paypal answer, received %s instead of VERIFIED/SUCCESS or INVALID/FAIL (validation: %s ) ' % ( resp , ' PDT ' if pdt_request else ' IPN/DPN ' ) )
2013-11-22 11:48:31 +00:00
return res
2013-11-07 18:04:44 +00:00
2014-01-29 17:15:20 +00:00
@http.route ( ' /payment/paypal/ipn/ ' , type = ' http ' , auth = ' none ' , methods = [ ' POST ' ] )
2013-11-22 11:48:31 +00:00
def paypal_ipn ( self , * * post ) :
""" Paypal IPN. """
_logger . info ( ' Beginning Paypal IPN form_feedback with post data %s ' , pprint . pformat ( post ) ) # debug
self . paypal_validate_data ( * * post )
2013-11-07 18:04:44 +00:00
return ' '
2016-09-12 07:24:38 +00:00
@http.route ( ' /payment/paypal/dpn ' , type = ' http ' , auth = " none " , methods = [ ' POST ' , ' GET ' ] )
2013-11-15 13:31:33 +00:00
def paypal_dpn ( self , * * post ) :
2013-11-22 11:48:31 +00:00
""" Paypal DPN """
_logger . info ( ' Beginning Paypal DPN form_feedback with post data %s ' , pprint . pformat ( post ) ) # debug
2013-12-04 16:07:38 +00:00
return_url = self . _get_return_url ( * * post )
2013-11-22 11:48:31 +00:00
self . paypal_validate_data ( * * post )
2014-01-24 14:59:23 +00:00
return werkzeug . utils . redirect ( return_url )
2013-11-15 13:31:33 +00:00
2014-01-29 17:15:20 +00:00
@http.route ( ' /payment/paypal/cancel ' , type = ' http ' , auth = " none " )
2013-11-15 13:31:33 +00:00
def paypal_cancel ( self , * * post ) :
2013-12-04 16:07:38 +00:00
""" When the user cancels its Paypal payment: GET on this route """
2014-01-29 17:15:20 +00:00
cr , uid , context = request . cr , SUPERUSER_ID , request . context
2013-12-04 16:07:38 +00:00
_logger . info ( ' Beginning Paypal cancel with post data %s ' , pprint . pformat ( post ) ) # debug
return_url = self . _get_return_url ( * * post )
2014-01-24 14:59:23 +00:00
return werkzeug . utils . redirect ( return_url )