[IMP] website_sale: improved checkout process
- now going to shop/confirmation when coming back from the acquirer - added poll on the confirmation page to wait for data from acquirer - misc cleaning of checkout process, order and transaction management - added cancel state on payment.transaction, when canceled by the customer bzr revid: tde@openerp.com-20131119160129-fkwkhjvk1bh0uarf
This commit is contained in:
parent
ba14bbe339
commit
4f6e792733
|
@ -149,11 +149,16 @@ class PaymentTransaction(osv.Model):
|
|||
string='Type', required=True),
|
||||
'state': fields.selection(
|
||||
[('draft', 'Draft'), ('pending', 'Pending'),
|
||||
('done', 'Done'), ('error', 'Error')],
|
||||
'Status', required=True,
|
||||
('done', 'Done'), ('error', 'Error'),
|
||||
('cancel', 'Canceled')
|
||||
], 'Status', required=True,
|
||||
track_visiblity='onchange'),
|
||||
'state_message': fields.text('Message',
|
||||
help='Field used to store error and/or validation messages for information'),
|
||||
# link with a record e.g. sale order
|
||||
# 'feedback_model': fields.char('Model'),
|
||||
# 'feedback_res_id': fields.integer('Res Id'),
|
||||
# 'feedback_method': fields.char('Method'), # use a return url with a dedicated controler ?
|
||||
# payment
|
||||
'amount': fields.float('Amount', required=True,
|
||||
help='Amount in cents',
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
# -*- 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.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
# from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError
|
||||
from openerp.addons.website.models import website
|
||||
|
||||
import logging
|
||||
# import requests
|
||||
# from urllib import urlencode
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OgoneController(http.Controller):
|
||||
_accept_url = '/payment/ogone/test/accept'
|
||||
|
@ -39,6 +12,9 @@ class OgoneController(http.Controller):
|
|||
|
||||
@website.route([
|
||||
'/payment/ogone/accept', '/payment/ogone/test/accept',
|
||||
'/payment/ogone/decline', '/payment/ogone/test/decline',
|
||||
'/payment/ogone/exception', '/payment/ogone/test/exception',
|
||||
'/payment/ogone/cancel', '/payment/ogone/test/cancel',
|
||||
], type='http', auth='admin')
|
||||
def ogone_form_feedback(self, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
|
@ -50,15 +26,3 @@ class OgoneController(http.Controller):
|
|||
res = Payment.ogone_form_feedback(cr, uid, post, context)
|
||||
print 'result after feedback', res
|
||||
return request.redirect(return_url)
|
||||
|
||||
@website.route([
|
||||
'/payment/ogone/decline', '/payment/ogone/test/decline',
|
||||
'/payment/ogone/exception', '/payment/ogone/test/exception',
|
||||
'/payment/ogone/cancel', '/payment/ogone/test/cancel',
|
||||
], type='http', auth='admin')
|
||||
def ogone_form_feedback_other(self, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
print 'Entering ogone_form_feedback_other', post
|
||||
return_url = post.pop('return_url', '/')
|
||||
print 'return_url', return_url
|
||||
return request.redirect(return_url)
|
||||
|
|
|
@ -154,6 +154,11 @@ class PaymentAcquirerOgone(osv.Model):
|
|||
|
||||
class PaymentTxOgone(osv.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
# ogone status
|
||||
_ogone_valid_tx_status = [5, 9]
|
||||
_ogone_wait_tx_status = [41, 50, 51, 52, 55, 56, 91, 92, 99]
|
||||
_ogone_pending_tx_status = [46] # 3DS HTML response
|
||||
_ogone_cancel_tx_status = [1]
|
||||
|
||||
_columns = {
|
||||
'ogone_3ds': fields.dummy('3ds Activated'),
|
||||
|
@ -231,13 +236,21 @@ class PaymentTxOgone(osv.Model):
|
|||
return False
|
||||
|
||||
status = int(data.get('STATUS', '0'))
|
||||
if status in [5, 9]:
|
||||
if status in self._ogone_valid_tx_status:
|
||||
tx.write({
|
||||
'state': 'done',
|
||||
'date_validate': data['TRXDATE'],
|
||||
'ogone_payid': data['PAYID'],
|
||||
})
|
||||
return True
|
||||
elif status in self._ogone_cancel_tx_status:
|
||||
tx.write({
|
||||
'state': 'cancel',
|
||||
})
|
||||
elif status in self._ogone_pending_tx_status:
|
||||
tx.write({
|
||||
'state': 'pending',
|
||||
})
|
||||
else:
|
||||
error = 'Ogone: feedback error: %(error_str)s\n\n%(error_code)s: %(error_msg)s' % {
|
||||
'error_str': data.get('NCERROR'),
|
||||
|
|
|
@ -412,6 +412,8 @@ class Ecommerce(http.Controller):
|
|||
order_obj = request.registry.get('sale.order')
|
||||
|
||||
order = request.registry.get('website').get_current_order(request.cr, request.uid, context=request.context)
|
||||
if not order:
|
||||
order = request.registry.get('website')._get_order(request.cr, request.uid, context=request.context)
|
||||
|
||||
request.context = dict(request.context, pricelist=self.get_pricelist())
|
||||
|
||||
|
@ -652,45 +654,50 @@ class Ecommerce(http.Controller):
|
|||
def payment(self, payment_acquirer_id=None, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
payment_obj = request.registry.get('payment.acquirer')
|
||||
order = request.registry['website'].get_current_order(cr, uid, context=context)
|
||||
|
||||
# if no sale order at this stage: back to checkout beginning
|
||||
order = context.get('website_sale_order')
|
||||
if not order or not order.order_line:
|
||||
return request.redirect("/shop/checkout/")
|
||||
|
||||
if 'website_sale_order' in context:
|
||||
shipping_pid = context['website_sale_order'].partner_id.id
|
||||
else:
|
||||
shipping_pid = False
|
||||
# alread a transaction: forward to confirmation
|
||||
tx = context.get('website_sale_transaction')
|
||||
if tx and not tx.state == 'draft':
|
||||
return request.redirect('/shop/confirmation')
|
||||
|
||||
partner_id = False
|
||||
shipping_partner_id = False
|
||||
if order:
|
||||
if order.partner_id.id:
|
||||
partner_id = order.partner_id.id
|
||||
shipping_partner_id = order.partner_id.id
|
||||
if order.partner_shipping_id.id:
|
||||
shipping_partner_id = order.partner_shipping_id.id
|
||||
|
||||
values = {
|
||||
'partner': shipping_pid,
|
||||
'partner': partner_id,
|
||||
'payment_acquirer_id': payment_acquirer_id,
|
||||
'order': order
|
||||
}
|
||||
values.update(request.registry.get('sale.order')._get_website_data(cr, uid, order, context))
|
||||
|
||||
# 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
|
||||
|
||||
# fetch all registered payment means
|
||||
if not values['payment_acquirer_id']:
|
||||
payment_ids = payment_obj.search(request.cr, SUPERUSER_ID, [('portal_published', '=', True)], context=request.context)
|
||||
values['payments'] = payment_obj.browse(cr, uid, payment_ids, context=context)
|
||||
for pay in values['payments']:
|
||||
pay._content = payment_obj.render(
|
||||
cr, uid, pay.id,
|
||||
order.name,
|
||||
order.amount_total,
|
||||
order.pricelist_id.currency_id,
|
||||
partner_id=shipping_pid,
|
||||
tx_custom_values={
|
||||
'return_url': '/shop/payment',
|
||||
},
|
||||
context=context)
|
||||
if tx:
|
||||
payment_ids = [tx.acquirer_id.id]
|
||||
else:
|
||||
payment_ids = payment_obj.search(cr, SUPERUSER_ID, [('portal_published', '=', True)], context=context)
|
||||
values['payments'] = payment_obj.browse(cr, uid, payment_ids, context=context)
|
||||
for pay in values['payments']:
|
||||
pay._content = payment_obj.render(
|
||||
cr, uid, pay.id,
|
||||
order.name,
|
||||
order.amount_total,
|
||||
order.pricelist_id.currency_id,
|
||||
partner_id=shipping_partner_id,
|
||||
tx_custom_values={
|
||||
'return_url': '/shop/confirmation',
|
||||
},
|
||||
context=context)
|
||||
|
||||
return request.website.render("website_sale.payment", values)
|
||||
|
||||
|
@ -701,7 +708,7 @@ class Ecommerce(http.Controller):
|
|||
|
||||
:param int acquirer_id: id of a payment.acquirer record. If not set the
|
||||
user is redirected to the checkout page
|
||||
:param dict post: should coutain only post data used by the acquirer
|
||||
:param dict post: should coutain all post data for the acquirer
|
||||
"""
|
||||
# @TDEFIXME: don't know why we received those data, but should not be send to the acquirer
|
||||
post.pop('submit.x', None)
|
||||
|
@ -715,11 +722,9 @@ class Ecommerce(http.Controller):
|
|||
return request.redirect("/shop/checkout/")
|
||||
|
||||
# find an already existing transaction
|
||||
tx_ids = transaction_obj.search(cr, uid, [
|
||||
('reference', '=', order.name), ('acquirer_id', '=', acquirer_id)
|
||||
], context=context)
|
||||
if not tx_ids:
|
||||
transaction_obj.create(cr, uid, {
|
||||
tx = context.get('website_sale_transaction')
|
||||
if not tx:
|
||||
tx_id = transaction_obj.create(cr, uid, {
|
||||
'acquirer_id': acquirer_id,
|
||||
'type': 'form',
|
||||
'amount': order.amount_total,
|
||||
|
@ -727,32 +732,48 @@ class Ecommerce(http.Controller):
|
|||
'partner_id': order.partner_id.id,
|
||||
'reference': order.name,
|
||||
}, context=context)
|
||||
request.httprequest.session['website_sale_transaction_id'] = tx_id
|
||||
elif tx and tx.state == 'draft': # button cliked but no more info -> rewrite on tx or create a new one ?
|
||||
tx.write({
|
||||
'acquirer_id': acquirer_id,
|
||||
})
|
||||
|
||||
acquirer_form_post_url = payment_obj.get_form_action_url(cr, uid, acquirer_id, context=context)
|
||||
acquirer_total_url = '%s?%s' % (acquirer_form_post_url, urllib.urlencode(post))
|
||||
return request.redirect(acquirer_total_url)
|
||||
|
||||
# @website.route(['/shop/payment_validation/'], type='json', auth="public", multilang=True)
|
||||
# def payment_validation(self, payment_acquirer_id=None, **post):
|
||||
# payment_obj = request.registry.get('payment.acquirer')
|
||||
# order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
# 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/payment/transaction/get_status',
|
||||
'/shop/payment/transaction/get_status/<int:transaction_>'
|
||||
], type='json', auth="public", multilang=True)
|
||||
def payment_validation(self, transaction_id=None, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
payment_obj = request.registry.get('payment.transaction')
|
||||
if transaction_id:
|
||||
tx = payment_obj.browse(cr, uid, transaction_id, context=context)
|
||||
else:
|
||||
tx = context.get('website_sale_transaction')
|
||||
return {
|
||||
'state': tx.state,
|
||||
}
|
||||
|
||||
@website.route(['/shop/confirmation/'], type='http', auth="public", multilang=True)
|
||||
def payment_confirmation(self, **post):
|
||||
order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
context = request.context
|
||||
|
||||
# if no sale order at this stage: back to shop
|
||||
order = context.get('website_sale_order')
|
||||
if not order or not order.order_line:
|
||||
return request.redirect("/shop/")
|
||||
|
||||
# no transaction: back to payment
|
||||
tx = context.get('website_sale_transaction')
|
||||
if not tx:
|
||||
return request.redirect('/shop/payment')
|
||||
|
||||
res = request.website.render("website_sale.confirmation", {'order': order})
|
||||
request.httprequest.session['ecommerce_order_id'] = False
|
||||
request.httprequest.session['ecommerce_pricelist'] = False
|
||||
# request.httprequest.session['ecommerce_order_id'] = False
|
||||
# request.httprequest.session['ecommerce_pricelist'] = False
|
||||
return res
|
||||
|
||||
@website.route(['/shop/change_sequence/'], type='json', auth="public")
|
||||
|
|
|
@ -13,9 +13,10 @@ class Website(osv.Model):
|
|||
order_obj = request.registry.get('sale.order')
|
||||
# check if order allready exists and have access
|
||||
if order_id:
|
||||
if not order_id in order_obj.exists(cr, uid, [order_id], context=context):
|
||||
return False
|
||||
try:
|
||||
order = order_obj.browse(cr, uid, order_id, context=context)
|
||||
order.pricelist_id
|
||||
if order:
|
||||
return order
|
||||
except:
|
||||
|
@ -48,11 +49,25 @@ class Website(osv.Model):
|
|||
return order
|
||||
return False
|
||||
|
||||
def _get_transaction(self, cr, uid, tx_id=None, context=None):
|
||||
transaction_obj = request.registry['payment.transaction']
|
||||
if tx_id:
|
||||
tx_ids = transaction_obj.search(cr, uid, [('id', '=', tx_id), ('state', 'not in', ['cancel'])], context=context)
|
||||
if tx_ids:
|
||||
return transaction_obj.browse(cr, uid, tx_ids[0], context=context)
|
||||
return False
|
||||
|
||||
def get_current_transaction(self, cr, uid, context=None):
|
||||
pass
|
||||
if request.httprequest.session.get('website_sale_transaction_id'):
|
||||
tx = self._get_transaction(cr, uid, tx_id=request.httprequest.session['website_sale_transaction_id'], context=context)
|
||||
if not tx:
|
||||
request.httprequest.session['website_sale_transaction_id'] = False
|
||||
return tx
|
||||
return False
|
||||
|
||||
def preprocess_request(self, cr, uid, ids, request, context=None):
|
||||
request.context.update({
|
||||
'website_sale_order': self.get_current_order(cr, uid, context=context),
|
||||
'website_sale_transaction': self.get_current_transaction(cr, uid, context=context)
|
||||
})
|
||||
return super(Website, self).preprocess_request(cr, uid, ids, request, context=None)
|
||||
|
|
|
@ -11,9 +11,4 @@ $(document).ready(function () {
|
|||
console.log('cliking on submit for payment - redirecting from', form_action, 'to shop with acqurier_id', acquirer_id);
|
||||
$(this).closest("form").attr("action", '/shop/payment/transaction/' + acquirer_id + '/');
|
||||
});
|
||||
|
||||
// openerp.jsonRpc('/shop/payment/transaction', 'call', {
|
||||
// }).then(function (result) {
|
||||
// console.log(result);
|
||||
// });
|
||||
});
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
$(document).ready(function () {
|
||||
|
||||
var _poll_nbr = 0;
|
||||
|
||||
function payment_transaction_poll_status() {
|
||||
return openerp.jsonRpc('/shop/payment/transaction/get_status', 'call', {
|
||||
}).then(function (result) {
|
||||
_poll_nbr += 1;
|
||||
if (result.state == 'done') {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>Thanks for your order</h2>";
|
||||
});
|
||||
}
|
||||
else if (result.state == 'pending') {
|
||||
if (_poll_nbr <= 10) {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>Waiting validation ...</h2>";
|
||||
});
|
||||
setTimeout(function () {
|
||||
return payment_transaction_poll_status();
|
||||
}, 1000);
|
||||
}
|
||||
else {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>You payment is currently under review. Please come back later.</h2>";
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (result.state == 'cancel') {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>The payment seems to have been canceled.</h2>";
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
payment_transaction_poll_status();
|
||||
});
|
|
@ -924,6 +924,7 @@
|
|||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_validate.js"></script>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="additional_title">Shop - Confirmed</t>
|
||||
|
@ -1011,8 +1012,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Tanks you for your order.</h2>
|
||||
<a href="/shop/payment_validate/" class="btn btn-primary mt16">Validate & Pay <span class="icon-long-arrow-right"/></a>
|
||||
<div class="oe_website_sale_confirmation">
|
||||
<h2>Tanks you for your order.</h2>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
|
|
Loading…
Reference in New Issue