diff --git a/addons/payment_acquirer_paypal/data/paypal.xml b/addons/payment_acquirer_paypal/data/paypal.xml index eac0b78bb72..789b3c78aa5 100644 --- a/addons/payment_acquirer_paypal/data/paypal.xml +++ b/addons/payment_acquirer_paypal/data/paypal.xml @@ -7,8 +7,10 @@ test https://www.sandbox.paypal.com/cgi-bin/webscr - dummy - dummy + tde+paypal-facilitator@openerp.com + tde+paypal-facilitator_api1.openerp.com + AYf_uBATwly1C72DqE2njwDHmZI25UHcZMwvgvgICLkeQEgutvrhrg6y3KhZ + EJSDgxC_LuZ9oeG-Ud_oozfiDqqN3mUVLMmzPK71IZA3TM4taicUY2uaJYU1 diff --git a/addons/payment_acquirer_paypal/models/paypal.py b/addons/payment_acquirer_paypal/models/paypal.py index a908ac34e2d..8a9b9c40c19 100644 --- a/addons/payment_acquirer_paypal/models/paypal.py +++ b/addons/payment_acquirer_paypal/models/paypal.py @@ -1,12 +1,20 @@ # -*- coding: utf-'8' "-*-" +from openerp.addons.payment_acquirer.models import payment_acquirer from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError from openerp.addons.payment_acquirer_paypal.controllers.main import PaypalController from openerp.osv import osv, fields from openerp.tools.float_utils import float_compare +import base64 +try: + import simplejson as json +except ImportError: + import json import logging import urlparse +import urllib +import urllib2 _logger = logging.getLogger(__name__) @@ -17,16 +25,18 @@ class AcquirerPaypal(osv.Model): _columns = { 'paypal_email_id': fields.char('Email ID', required_if_provider='paypal'), 'paypal_username': fields.char('Username', required_if_provider='paypal'), - 'paypal_password': fields.char('Password'), - 'paypal_signature': fields.char('Signature'), 'paypal_tx_url': fields.char('Transaction URL', required_if_provider='paypal'), - 'paypal_use_dpn': fields.boolean('Use DPN'), 'paypal_use_ipn': fields.boolean('Use IPN'), + 'paypal_api_enabled': fields.boolean('Use Rest API'), + 'paypal_api_username': fields.char('Rest API Username'), + 'paypal_api_password': fields.char('Rest API Password'), + 'paypal_api_access_token': fields.char('Access Token'), + 'paypal_api_access_token_validity': fields.datetime('Access Token Validity'), } _defaults = { - 'paypal_use_dpn': True, 'paypal_use_ipn': True, + 'paypal_api_enabled': False, 'paypal_tx_url': 'https://www.sandbox.paypal.com/cgi-bin/webscr', } @@ -45,13 +55,13 @@ class AcquirerPaypal(osv.Model): 'item_number': reference, 'amount': amount, 'currency_code': currency and currency.name or 'EUR', - 'address1': partner and ' '.join((partner.street or '', partner.street2 or '')).strip() or ' '.join((partner_values.get('street', ''), partner_values.get('street2', ''))).strip(), + 'address1': payment_acquirer._partner_format_address(partner and partner.street or partner_values.get('street', ''), partner and partner.street2 or partner_values.get('street2', '')), 'city': partner and partner.city or partner_values.get('city', ''), 'country': partner and partner.country_id and partner.country_id.name or partner_values.get('country_name', ''), 'email': partner and partner.email or partner_values.get('email', ''), 'zip': partner and partner.zip or partner_values.get('zip', ''), - 'first_name': partner and partner.name or partner_values.get('name', '').split()[-1:], - 'last_name': partner and partner.name or partner_values.get('name', '').split()[:-1], + 'first_name': payment_acquirer._partner_split_name(partner and partner.name or partner_values.get('name', ''))[0], + 'last_name': payment_acquirer._partner_split_name(partner and partner.name or partner_values.get('name', ''))[1], 'return': '%s' % urlparse.urljoin(base_url, PaypalController._return_url), 'notify_url': '%s' % urlparse.urljoin(base_url, PaypalController._notify_url), 'cancel_return': '%s' % urlparse.urljoin(base_url, PaypalController._cancel_url), @@ -66,6 +76,31 @@ class AcquirerPaypal(osv.Model): acquirer = self.browse(cr, uid, id, context=context) return acquirer.paypal_tx_url + def _paypal_s2s_get_access_token(self, cr, uid, ids, context=None): + """ + Note: see # see http://stackoverflow.com/questions/2407126/python-urllib2-basic-auth-problem + for explanation why we use Authorization header instead of urllib2 + password manager + """ + res = dict() + parameters = urllib.urlencode({'grant_type': 'client_credentials'}) + request = urllib2.Request('https://api.sandbox.paypal.com/v1/oauth2/token', parameters) + # add other headers (https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/) + request.add_header('Accept', 'application/json') + request.add_header('Accept-Language', 'en_US') + + for acquirer in self.browse(cr, uid, ids, context=context): + # add authorization header + base64string = base64.encodestring('%s:%s' % ( + acquirer.paypal_api_username, + acquirer.paypal_api_password) + ).replace('\n', '') + request.add_header("Authorization", "Basic %s" % base64string) + + result = urllib2.urlopen(request).read() + res[acquirer.id] = json.loads(result).get('access_token') + return res + class TxPaypal(osv.Model): _inherit = 'payment.transaction' @@ -84,8 +119,8 @@ class TxPaypal(osv.Model): tx_data = { 'item_name': tx.name, - 'first_name': tx.partner_name and tx.partner_name.split()[-1:], - 'last_name': tx.partner_name and tx.partner_name.split()[:-1], + 'first_name': payment_acquirer._partner_split_name(tx.partner_name)[0], + 'last_name': payment_acquirer._partner_split_name(tx.partner_name)[0], 'email': tx.partner_email, 'zip': tx.partner_zip, 'address1': tx.partner_address, @@ -101,7 +136,7 @@ class TxPaypal(osv.Model): context=context ) - def _paypal_get_tx_from_data(self, cr, uid, data, context=None): + def _paypal_form_get_tx_from_data(self, cr, uid, data, context=None): reference, txn_id = data.get('item_number'), data.get('txn_id') if not reference or not txn_id: error_msg = 'Paypal: received data with missing reference (%s) or txn_id (%s)' % (reference, txn_id) @@ -118,16 +153,10 @@ class TxPaypal(osv.Model): error_msg += '; multiple order found' _logger.error(error_msg) raise ValidationError(error_msg) - tx = self.pool['payment.transaction'].browse(cr, uid, tx_ids[0], context=context) + return self.browse(cr, uid, tx_ids[0], context=context) - return tx - - def paypal_form_feedback(self, cr, uid, data, context=None): + def _paypal_form_get_invalid_parameters(self, cr, uid, tx, data, context=None): invalid_parameters = [] - - # get tx - tx = self._paypal_get_tx_from_data(cr, uid, data, context=context) - if data.get('notify_version')[0] != '2.6': _logger.warning( 'Received a notification from Paypal with version %s instead of 2.6. This could lead to issues when managing it.' % @@ -139,9 +168,9 @@ class TxPaypal(osv.Model): ), # check what is buyed if float_compare(float(data.get('mc_gross', '0.0')), tx.amount, 2) != 0: - invalid_parameters.append(('mc_gross', tx.amount)) + invalid_parameters.append(('mc_gross', data.get('mc_gross'), '%.2f' % tx.amount)) if data.get('mc_currency') != tx.currency_id.name: - invalid_parameters.append(('mc_currency', tx.currency_id.name)) + invalid_parameters.append(('mc_currency', data.get('mc_currency'), tx.currency_id.name)) # if parameters.get('payment_fee') != tx.payment_fee: # invalid_parameters.append(('payment_fee', tx.payment_fee)) # if parameters.get('quantity') != tx.quantity: @@ -159,33 +188,71 @@ class TxPaypal(osv.Model): # if parameters.get('receiver_id') != tx.receiver_id: # invalid_parameters.append(('receiver_id', tx.receiver_id)) - if invalid_parameters: - _warn_message = 'The following transaction parameters are incorrect:\n' - for item in invalid_parameters: - _warn_message += '\t%s: received %s instead of %s\n' % (item, data.get(item[0]), item[1]) - _logger.warning(_warn_message) - return False + return invalid_parameters - status = data.get('payment_status', 'Pending') + def _paypal_form_validate(self, cr, uid, tx, data, context=None): + status = data.get('payment_status') if status in ['Completed', 'Processed']: + _logger.info('Validated Paypal payment for tx %s: set as done' % (tx.reference)) tx.write({ 'state': 'done', - 'txn_id': data['txn_id'], 'date_validate': data.get('payment_date', fields.datetime.now()), - 'paypal_txn_type': data.get('express_checkout') + 'paypal_txn_id': data['txn_id'], + 'paypal_txn_type': data.get('express_checkout'), }) return True elif status in ['Pending', 'Expired']: + _logger.info('Received notification for Paypal payment %s: set as pending' % (tx.reference)) tx.write({ 'state': 'pending', - 'txn_id': data['txn_id'], + 'paypal_txn_id': data['txn_id'], + 'paypal_txn_type': data.get('express_checkout'), }) return True else: - error = 'Paypal: feedback error' + error = 'Received unrecognized status for Paypal payment %s: %s, set as error' % (tx.reference, status) _logger.info(error) tx.write({ 'state': 'error', - 'state_message': error + 'state_message': error, + 'paypal_txn_id': data['txn_id'], + 'paypal_txn_type': data.get('express_checkout'), }) return False + + # -------------------------------------------------- + # SERVER2SERVER RELATED METHODS + # -------------------------------------------------- + + def paypal_s2s_create(self, cr, uid, values, context=None): + tx_id = self.create(cr, uid, values, context=context) + tx = self.browse(cr, uid, tx_id, context=context) + + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer %s' % tx.acquirer_id._paypal_s2s_get_access_token()[tx.acquirer_id.id], + } + data = { + 'intent': 'sale', + 'redirect_urls': { + 'return_url': 'http://example.com/your_redirect_url/', + 'cancel_url': 'http://example.com/your_cancel_url/', + }, + 'payer': { + 'payment_method': 'paypal', + }, + 'transactions': [ + { + 'amount': { + 'total': '7.47', + 'currency': 'USD', + } + } + ] + } + data = json.dumps(data) + + request = urllib2.Request('https://api.sandbox.paypal.com/v1/payments/payment', data, headers) + + result = urllib2.urlopen(request).read() + return result diff --git a/addons/payment_acquirer_paypal/views/payment_acquirer.xml b/addons/payment_acquirer_paypal/views/payment_acquirer.xml index ae89253545c..d0d97cdfa12 100644 --- a/addons/payment_acquirer_paypal/views/payment_acquirer.xml +++ b/addons/payment_acquirer_paypal/views/payment_acquirer.xml @@ -12,9 +12,10 @@ attrs="{'invisible': [('name', '!=', 'paypal')]}"> - - + + + diff --git a/addons/payment_acquirer_paypal/views/paypal.xml b/addons/payment_acquirer_paypal/views/paypal.xml index 9515e0a12a2..329714fb0a5 100644 --- a/addons/payment_acquirer_paypal/views/paypal.xml +++ b/addons/payment_acquirer_paypal/views/paypal.xml @@ -23,11 +23,11 @@ - -