[WIP] payment_acquirer

bzr revid: chm@openerp.com-20131017110046-1b5pmju5q8x3gaz5
This commit is contained in:
Christophe Matthieu 2013-10-17 13:00:46 +02:00
parent 6e4c0f9068
commit 36675c3663
9 changed files with 231 additions and 77 deletions

View File

@ -19,5 +19,4 @@
#
##############################################################################
import controllers
import payment_acquirer

View File

@ -30,6 +30,8 @@
'data': [
'views/acquirer_view.xml',
'payment_acquirer_data.xml',
'security/ir.model.access.csv',
'security/ir.rule.xml',
],
'installable': True,
}

View File

@ -1 +0,0 @@
import main

View File

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

View File

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

View File

@ -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 &amp; Pay <span class="icon-long-arrow-right"/></a>
</div>
</template>

View File

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

View File

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

View File

@ -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 &amp; 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 &amp; 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>