[WIP] payment_acquirer
bzr revid: chm@openerp.com-20131017110046-1b5pmju5q8x3gaz5
This commit is contained in:
parent
6e4c0f9068
commit
36675c3663
|
@ -19,5 +19,4 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import controllers
|
||||
import payment_acquirer
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
'data': [
|
||||
'views/acquirer_view.xml',
|
||||
'payment_acquirer_data.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/ir.rule.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
import main
|
|
@ -1,32 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website.models import website
|
||||
|
||||
|
||||
class PaymentAcquirer(http.Controller):
|
||||
|
||||
@website.route(['/payment_acquirer/'], type='json', auth="public")
|
||||
def payment_acquirer(self, **post):
|
||||
return False
|
|
@ -22,13 +22,66 @@
|
|||
import openerp
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools import float_repr
|
||||
import urlparse
|
||||
import requests
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class type(osv.osv):
|
||||
_name = 'payment.acquirer.type'
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True),
|
||||
}
|
||||
def validate_payement(self, cr, uid, id, object, reference, currency, amount, context=None):
|
||||
"""
|
||||
return (payment, retry_time)
|
||||
payment: "validated" or "refused" or "pending"
|
||||
retry_time = False (don't retry validation) or int (seconds for retry validation)
|
||||
"""
|
||||
if isinstance(id, list):
|
||||
id = id[0]
|
||||
pay_type = self.browse(cr, uid, id, context=context)
|
||||
method = getattr(self, '_validate_payement_%s' % pay_type.name)
|
||||
return method(object, reference, currency, amount, context=context)
|
||||
|
||||
def _validate_payement_virement(self, object, reference, currency, amount, context=None):
|
||||
return ("pending", False)
|
||||
|
||||
|
||||
class type_paypal(osv.osv):
|
||||
_inherit = "payment.acquirer.type"
|
||||
|
||||
def _validate_payement_paypal(self, object, reference, currency, amount, context=None):
|
||||
parameters = {}
|
||||
parameters.update(
|
||||
cmd='_notify-validate',
|
||||
business=object.company_id.paypal_account,
|
||||
item_name="%s %s" % (object.company_id.name, reference),
|
||||
item_number=reference,
|
||||
amount=amount,
|
||||
currency_code=currency.name
|
||||
)
|
||||
paypal_url = "https://www.paypal.com/cgi-bin/webscr"
|
||||
paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr"
|
||||
response = urlparse.parse_qsl(requests.post(paypal_url, data=parameters))
|
||||
|
||||
# transaction's unique id
|
||||
# response["txn_id"]
|
||||
|
||||
if response["payment_status"] == "Voided":
|
||||
raise "Paypal authorization has been voided."
|
||||
elif response["payment_status"] in ("Completed", "Processed") and response["item_number"] == reference and response["mc_gross"] == amount:
|
||||
return ("validated", False)
|
||||
elif response["payment_status"] == "Expired":
|
||||
_logger.warn("Paypal Validate Payement status: Expired")
|
||||
return ("pending", 5)
|
||||
elif response["payment_status"] == "Pending":
|
||||
_logger.warn("Paypal Validate Payement status: Pending, reason: %s" % response["pending_reason"])
|
||||
return ("pending", 5)
|
||||
|
||||
# Canceled_Reversal, Denied, Failed, Refunded, Reversed
|
||||
return ("refused", False)
|
||||
|
||||
|
||||
class acquirer(osv.osv):
|
||||
|
@ -46,10 +99,10 @@ class acquirer(osv.osv):
|
|||
'visible': True,
|
||||
}
|
||||
|
||||
def render(self, cr, uid, ids, object, reference, currency, amount, context=None):
|
||||
def render(self, cr, uid, id, object, reference, currency, amount, cancel_url=None, return_url=None, context=None):
|
||||
""" Renders the form template of the given acquirer as a qWeb template """
|
||||
user = self.pool.get("res.users")
|
||||
precision = self.pool.get("decimal.precision").precision_get(cr, uid, 'Account')
|
||||
precision = self.pool.get("decimal.precision").precision_get(cr, openerp.SUPERUSER_ID, 'Account')
|
||||
|
||||
if not context:
|
||||
context = {}
|
||||
|
@ -62,31 +115,22 @@ class acquirer(osv.osv):
|
|||
amount=amount,
|
||||
amount_str=float_repr(amount, precision),
|
||||
user_id=user.browse(cr, uid, uid),
|
||||
context=context
|
||||
context=context,
|
||||
cancel_url=cancel_url,
|
||||
return_url=return_url
|
||||
)
|
||||
|
||||
pays = self.browse(cr, uid, ids, context=context)
|
||||
|
||||
if isinstance(ids, list):
|
||||
res = []
|
||||
for pay in pays:
|
||||
res[pay.id] = pay.form_template_id.render(qweb_context.copy(), engine='ir.qweb', context=context)
|
||||
return res
|
||||
else:
|
||||
return pays.form_template_id.render(qweb_context, engine='ir.qweb', context=context)
|
||||
|
||||
def validate_payement(self, cr, uid, ids, object, reference, currency, amount, context=None):
|
||||
res = []
|
||||
for pay in self.browse(cr, uid, ids, context=context):
|
||||
method = getattr(self, '_validate_payement_%s' % pay.type_id.name)
|
||||
res[pay.id] = method(cr, uid, ids, object, reference, currency, amount, context=context)
|
||||
return res
|
||||
|
||||
def _validate_payement_paypal(self, cr, uid, ids, object, reference, currency, amount, context=None):
|
||||
payment = "pending" # "validated" or "refused" or "pending"
|
||||
retry_time = False
|
||||
return self.browse(cr, uid, id, context=context) \
|
||||
.form_template_id.render(qweb_context, engine='ir.qweb', context=context) \
|
||||
.strip()
|
||||
|
||||
def validate_payement(self, cr, uid, id, object, reference, currency, amount, context=None):
|
||||
"""
|
||||
return (payment, retry_time)
|
||||
|
||||
def _validate_payement_virement(self, cr, uid, ids, object, reference, currency, amount, context=None):
|
||||
return ("pending", False)
|
||||
payment: "validated" or "refused" or "pending"
|
||||
retry_time = False (don't retry validation) or int (seconds for retry validation)
|
||||
"""
|
||||
if isinstance(id, list):
|
||||
id = id[0]
|
||||
type_id = self.browse(cr, uid, id, context=context).type_id
|
||||
return type_id.validate_payement(object, reference, currency, amount, context=context)
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
<!-- Paypal -->
|
||||
|
||||
<template id="paypal_acquirer_view">
|
||||
<form t-if="object.company_id.paypal_account" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
|
||||
<form t-if="object.company_id.paypal_account" action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post" target="_self">
|
||||
<input type="hidden" name="cmd" value="_xclick"/>
|
||||
<input type="hidden" name="business" t-att-value="object.company_id.paypal_account"/>
|
||||
<input type="hidden" name="item_name" t-attf-value="#{object.company_id.name} #{reference}"/>
|
||||
<input type="hidden" name="item_number" t-att-value="reference"/>
|
||||
<input type="hidden" name="amount" t-att-value="amount"/>
|
||||
<input type="hidden" name="currency_code" t-att-value="currency.name"/>
|
||||
<input t-if="return_url" type="hidden" name="return_url" t-att-value="return_url"/>
|
||||
<input t-if="cancel_url" type="hidden" name="cancel_url" t-att-value="cancel_url"/>
|
||||
<input type="image" name="submit" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"/>
|
||||
</form>
|
||||
</template>
|
||||
|
@ -36,6 +39,7 @@
|
|||
<tr><td>Numero de compte: </td><td></td></tr>
|
||||
<tr><td>Communication: </td><td><t t-esc="reference"/></td></tr>
|
||||
</table>
|
||||
<a t-att-href="return_url" class="btn btn-primary">Validate & Pay <span class="icon-long-arrow-right"/></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -613,31 +613,63 @@ class Ecommerce(http.Controller):
|
|||
return request.redirect("/shop/payment/")
|
||||
|
||||
@website.route(['/shop/payment/'], type='http', auth="public", multilang=True)
|
||||
def payment(self, **post):
|
||||
def payment(self, payment_acquirer_id=None, **post):
|
||||
payment_obj = request.registry.get('payment.acquirer')
|
||||
order = get_current_order()
|
||||
|
||||
if not order or not order.order_line:
|
||||
return self.mycart(**post)
|
||||
return request.redirect("/shop/checkout/")
|
||||
|
||||
values = {
|
||||
'partner': False,
|
||||
'payment_acquirer_id': payment_acquirer_id,
|
||||
'order': order
|
||||
}
|
||||
|
||||
payment_obj = request.registry.get('payment.acquirer')
|
||||
payment_ids = payment_obj.search(request.cr, SUPERUSER_ID, [('visible', '=', True)], context=request.context)
|
||||
values['payments'] = payment_obj.browse(request.cr, SUPERUSER_ID, payment_ids, request.context)
|
||||
for payment in values['payments']:
|
||||
content = payment_obj.render(request.cr, SUPERUSER_ID, payment.id, order, order.name, order.pricelist_id.currency_id, order.amount_total)
|
||||
payment._content = content.strip()
|
||||
if payment_acquirer_id:
|
||||
values['validation'] = self.payment_validation(payment_acquirer_id)
|
||||
if values['validation'][0] == "validated" or (values['validation'][0] == "pending" and values['validation'][1] == False):
|
||||
return request.redirect("/shop/confirmation/")
|
||||
elif values['validation'][0] == "refused":
|
||||
values['payment_acquirer_id'] = None
|
||||
|
||||
if not values['payment_acquirer_id']:
|
||||
payment_ids = payment_obj.search(request.cr, SUPERUSER_ID, [('visible', '=', True)], context=request.context)
|
||||
values['payments'] = payment_obj.browse(request.cr, request.uid, payment_ids, context=request.context)
|
||||
for pay in values['payments']:
|
||||
pay._content = payment_obj.render(request.cr, request.uid, pay.id,
|
||||
object=order,
|
||||
reference=order.name,
|
||||
currency=order.pricelist_id.currency_id,
|
||||
amount=order.amount_total,
|
||||
cancel_url=request.httprequest.base_url,
|
||||
return_url="%sshop/payment/?payment_acquirer_id=%s" % (request.httprequest.host_url, pay.id),
|
||||
context=request.context)
|
||||
|
||||
return request.website.render("website_sale.payment", values)
|
||||
|
||||
@website.route(['/shop/payment_validate/'], type='http', auth="public", multilang=True)
|
||||
def payment_validate(self, **post):
|
||||
@website.route(['/shop/payment_validation/'], type='json', auth="public", multilang=True)
|
||||
def payment_validation(self, payment_acquirer_id=None):
|
||||
payment_obj = request.registry.get('payment.acquirer')
|
||||
order = get_current_order()
|
||||
return payment_obj.validate_payement(request.cr, request.uid, int(payment_acquirer_id),
|
||||
object=order,
|
||||
reference=order.name,
|
||||
currency=order.pricelist_id.currency_id,
|
||||
amount=order.amount_total,
|
||||
context=request.context)
|
||||
|
||||
@website.route(['/shop/confirmation/'], type='http', auth="public", multilang=True)
|
||||
def confirmation(self, **post):
|
||||
order = get_current_order()
|
||||
|
||||
if not order or not order.order_line:
|
||||
return request.redirect("/shop/")
|
||||
|
||||
res = request.website.render("website_sale.confirmation", {'order': order})
|
||||
request.httprequest.session['ecommerce_order_id'] = False
|
||||
request.httprequest.session['ecommerce_pricelist'] = False
|
||||
return request.redirect("/shop/")
|
||||
return res
|
||||
|
||||
@website.route(['/shop/change_sequence/'], type='json', auth="public")
|
||||
def change_sequence(self, id, top):
|
||||
|
|
|
@ -35,8 +35,8 @@ class attributes(osv.Model):
|
|||
'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values'),
|
||||
'product_ids': fields.one2many('product.attribute.product', 'attribute_id', 'Products'),
|
||||
|
||||
'float_max': fields.function(_get_float_max, type='float', string="Max", relation='product.attribute.product',store=True),
|
||||
'float_min': fields.function(_get_float_min, type='float', string="Min", relation='product.attribute.product',store=True),
|
||||
'float_max': fields.function(_get_float_max, type='float', string="Max", relation='product.attribute.product'),
|
||||
'float_min': fields.function(_get_float_min, type='float', string="Min", relation='product.attribute.product'),
|
||||
}
|
||||
_defaults = {
|
||||
'type': 'distinct'
|
||||
|
|
|
@ -789,7 +789,7 @@
|
|||
</ul>
|
||||
<h1 class="mb32">Validate Order</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-8 oe_mycart">
|
||||
<div class="col-md-8 oe_mycart">
|
||||
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
|
||||
<colgroup>
|
||||
<col width="80"/>
|
||||
|
@ -869,7 +869,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="js_payment mb64">
|
||||
<div class="js_payment mb64" t-if="not payment_acquirer_id and payments">
|
||||
<p>Select your payment method:</p>
|
||||
<div>
|
||||
<t t-foreach="payments" t-as="payment">
|
||||
|
@ -881,9 +881,115 @@
|
|||
<t t-foreach="payments" t-as="payment">
|
||||
<div t-att-data-id="payment.id" t-raw="payment._content" class="hidden"/>
|
||||
</t>
|
||||
<a t-href="/shop/payment_validate/" class="btn btn-primary mt16">Validate & Pay <span class="icon-long-arrow-right"/></a>
|
||||
</div>
|
||||
|
||||
<div class="js_payment_validation mb64" t-if="payment_acquirer_id">
|
||||
Please wait, we validate your payment.
|
||||
<div>
|
||||
<t t-esc="validation"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="confirmation">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="additional_title">Shop - Confirmed</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_sale">
|
||||
|
||||
<ul class="wizard pull-right">
|
||||
<li class="text-muted">Review Order<span class="chevron"></span></li>
|
||||
<li class="text-muted">Shipping & Billing<span class="chevron"></span></li>
|
||||
<li class="text-muted">Payment<span class="chevron"></span></li>
|
||||
<li class="text-primary">Confirmation<span class="chevron"></span></li>
|
||||
</ul>
|
||||
<h1 class="mb32">Validate Order</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-8 oe_mycart">
|
||||
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
|
||||
<colgroup>
|
||||
<col width="80"/>
|
||||
<col/>
|
||||
<col width="100"/>
|
||||
<col width="120"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">Product</th>
|
||||
<th>Price</th>
|
||||
<th>Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="website_sale_order.order_line" t-as="line">
|
||||
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/">
|
||||
<span t-field="line.product_id.image_small"
|
||||
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
|
||||
</a>
|
||||
</td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<strong t-field="line.product_id.name"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-field="line.price_unit" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
<div t-esc="line.product_uom_qty"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class='pull-right mb16' id="mycart_total">
|
||||
<colgroup>
|
||||
<col width="100"/>
|
||||
<col width="120"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr style="border-top: 1px solid #000">
|
||||
<th><h3>Total:</h3></th>
|
||||
<th class="text-right"><h3><span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></h3></th>
|
||||
</tr>
|
||||
<tr class="text-muted">
|
||||
<td><abbr title="Taxes may be updated after providing shipping address">Incl. Taxes:</abbr></td>
|
||||
<td class="text-right"><span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="clearfix"/>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-1 text-muted" id="right_column">
|
||||
<h4>Bill To:</h4>
|
||||
<div t-field="website_sale_order.partner_invoice_id"/>
|
||||
|
||||
<h4 class="mt32">Ship To:</h4>
|
||||
<div t-field="website_sale_order.partner_shipping_id"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Tanks you for your order.</h2>
|
||||
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue