|
|
|
@@ -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
|
|
|
|
|