diff --git a/acquirer_payplug/.DS_Store b/acquirer_payplug/.DS_Store new file mode 100644 index 0000000..5b29528 Binary files /dev/null and b/acquirer_payplug/.DS_Store differ diff --git a/acquirer_payplug/__init__.py b/acquirer_payplug/__init__.py new file mode 100644 index 0000000..9e4e40a --- /dev/null +++ b/acquirer_payplug/__init__.py @@ -0,0 +1,11 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import controllers +from . import models + +from odoo.addons.payment import setup_provider, reset_payment_provider + +def post_init_hook(cr, registry): + setup_provider(cr, registry, 'payplug') + +def uninstall_hook(cr, registry): + reset_payment_provider(cr, registry, 'payplug') \ No newline at end of file diff --git a/acquirer_payplug/__manifest__.py b/acquirer_payplug/__manifest__.py new file mode 100644 index 0000000..09b8b3b --- /dev/null +++ b/acquirer_payplug/__manifest__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'PayPlug Multi-Payments', + 'category': 'Accounting/Payment Acquirers', + 'summary': 'Several types of payments with PayPlug', + 'license': 'LGPL-3', + 'version': '3.1', + 'author': 'PayPlug & Applylog', + 'website': 'www.applylog.com', + 'description': """ + - PayPlug Payment by credit cards. + - PayLater 3x 4x by credit cards. + - AmEx Payment by credit cards. + - Bancontact Payment by credit cards. + """, + 'depends': [ + 'payment', + 'account_payment', + 'sale_management', + 'website', + 'website_sale', + 'website_payment', + ], + 'data': [ + 'views/payment_transaction_view.xml', + 'views/payment_provider_view.xml', + 'views/payment_provider_template.xml', + 'views/payment_provider_oney_template.xml', + 'datas/payment_icon_data.xml', + 'datas/payment_provider_data.xml', + ], + 'external_dependencies': { + 'python' : ['payplug==1.3.1'], + }, + 'assets': { + 'web.assets_frontend' : [ + 'acquirer_payplug/static/src/css/website_oney.css', + 'acquirer_payplug/static/src/js/website_oney.js', + ], + }, + 'images': [ + 'static/description/banner.png', + ], + 'application': False, + 'post_init_hook': 'post_init_hook', + 'uninstall_hook': 'uninstall_hook', +} \ No newline at end of file diff --git a/acquirer_payplug/controllers/__init__.py b/acquirer_payplug/controllers/__init__.py new file mode 100644 index 0000000..0d03d3c --- /dev/null +++ b/acquirer_payplug/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import main +from . import portal diff --git a/acquirer_payplug/controllers/main.py b/acquirer_payplug/controllers/main.py new file mode 100644 index 0000000..f6c3c9f --- /dev/null +++ b/acquirer_payplug/controllers/main.py @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- + +#from urllib.parse import urlparse, urljoin +#import werkzeug +from odoo import http +from odoo.http import request +from odoo import fields, http, tools, _ +import base64 +import requests +import json +import logging +_logger = logging.getLogger(__name__) +import payplug +payplug.set_api_version("2019-08-06") +import pprint +from odoo.addons.website_sale.controllers.main import WebsiteSale +from odoo.tools.json import scriptsafe as json_scriptsafe +from odoo.exceptions import AccessError, UserError, ValidationError + +class WebsiteSaleOney(WebsiteSale): + + # CREATE SIMULATION ONEY IN PRODUCT FORM + def _prepare_product_values(self, product, category, search, **kwargs): + res=super(WebsiteSaleOney,self)._prepare_product_values(product, category, search, **kwargs) + # IF PAYMENT ACQUIRER IS ONEY UPDATE VALUES + payment_provider=request.env['payment.provider'].sudo().search([('code','=','payplug')], limit=1) + # ONEY IS ACTIVE + if payment_provider.state != 'disabled' and payment_provider.payment_type_oney == True: + # IF THE PROVIDER IS ALLOWED FROM THE WEBSITE COMPANY + company_ids=request.env.context.get('allowed_company_ids') + if payment_provider.company_id.id in company_ids: + authorized_by_company = True + else: + authorized_by_company = False + # TO VERIFY THAT THE AMOUNT IS IN THE ONEY AUTHORIZED RANGE + amount = res.get('product').list_price + if amount < 100 or amount > 3000 : + authorized_by_oney = False + else: + authorized_by_oney = True + result_simulation=request.env['payment.provider'].sudo()._oney_payment_simulation(amount, 'FR') + # UPDATE DICT WITH SIMULATION ONEY + res.update(result_simulation) + res.update({ + 'InformationMessage': payment_provider.oney_information_message, + 'FooterMessage': payment_provider.oney_footer_message, + 'amount_total_oney': amount, + 'authorized_by_oney': authorized_by_oney, + 'authorized_by_company': authorized_by_company, + 'acquirer': payment_provider, + 'currency_symbol': request.website.currency_id.symbol, + }) + else: + res.update({ + 'InformationMessage': False, + 'FooterMessage': False, + 'amount_total_oney': 0, + 'authorized_by_oney': None, + 'authorized_by_company': None, + 'acquirer': payment_provider, + 'currency_symbol': request.website.currency_id.symbol, + }) + return res + + #UPDATE CART + @http.route(['/shop/cart'], type='http', auth="public", website=True, sitemap=False) + def cart(self, access_token=None, revive='', **post): + res=super(WebsiteSaleOney,self).cart(access_token, revive, **post) + # IF PAYMENT ACQUIRER IS ONEY UPDATE VALUES + payment_provider=request.env['payment.provider'].sudo().search([('code','=','payplug')], limit=1) + # ONEY IS ACTIVE + if payment_provider.state != 'disabled' and payment_provider.payment_type_oney == True: + amount=res.__dict__['qcontext']['website_sale_order'].amount_total + # IF THE PROVIDER IS ALLOWED FROM THE WEBSITE COMPANY + company_ids=request.env.context.get('allowed_company_ids') + if payment_provider.company_id.id in company_ids: + authorized_by_company = True + else: + authorized_by_company = False + if amount < 100 or amount > 3000 : + authorized_by_oney = False + else: + authorized_by_oney = True + currency=res.__dict__['qcontext']['website_sale_order'].currency_id.symbol + res.__dict__['qcontext'].update({ + 'InformationMessage': payment_provider.oney_information_message, + 'FooterMessage': payment_provider.oney_footer_message, + 'currency_symbol': request.website.currency_id.symbol, + 'amount_total_oney': amount, + 'authorized_by_oney': authorized_by_oney, + 'authorized_by_company': authorized_by_company, + }) + res.__dict__['qcontext'].update({'acquirer': payment_provider}) # IF ONEY IS DISABLED + return res + + # UPDATE ONEY SIMULATION + @http.route(['/shop/product/oney_simulation'], type='json', auth="public", website=True, csrf=False) + def oney_simulation(self, **kw): + values={} + payment_provider=request.env['payment.provider'].sudo().search([('code','=','payplug')], limit=1) + # ONEY IS ACTIVE + if payment_provider.state != 'disabled' and payment_provider.payment_type_oney == True: + result_simulation=request.env['payment.provider'].sudo()._oney_payment_simulation(float(kw.get('product_total_price')), 'FR') + values = { + 'amount_total_oney': "%.2f" %(float(kw.get('product_total_price'))), + 'payment_3x': result_simulation['payment_3x'], + 'payment_4x': result_simulation['payment_4x'], + } + return values + + # CREATE SIMULATION ONEY IN CART PAYMENT + def _get_shop_payment_values(self, order, **kwargs): + res = super(WebsiteSaleOney, self)._get_shop_payment_values(order, **kwargs) + # Return de specific values for Payment Oney + payment_provider = request.env['payment.provider'].sudo().search([('code','=','payplug')], limit=1) + if payment_provider.state != 'disabled': + country=order.partner_id.country_id + if payment_provider.payment_type_oney == True: + result_simulation=request.env['payment.provider'].sudo()._oney_payment_simulation(order.amount_total, 'FR') + res.update(result_simulation) + # ONEY PAYMENT CONTROL BETWEEN €100 AND €3000 + if order.amount_total >= 100.00 and order.amount_total <= 3000.00: + authorized_by_oney=True + else: + authorized_by_oney=False + # AUTHORIZED COUNTRY FOR ONEY + authorized_country_oney=True + if country not in payment_provider.available_oney_country_ids: + authorized_country_oney=False + total_qty_card=0.0 + limit_quantity=False + for line in res['website_sale_order'].order_line: + total_qty_card+=line.product_uom_qty + if total_qty_card > 10.0: + limit_quantity=True + authorized_by_oney=False + res.update({ + 'amount_oney': order.amount_total, + 'authorized_by_oney': authorized_by_oney, + 'limit_quantity': limit_quantity, + 'InformationMessage': payment_provider.oney_information_message, + 'type_oney': 'oney_x3_with_fees', + 'currency_symbol': order.currency_id.symbol, + 'authorized_country_oney': authorized_country_oney, + }) + # AUTHORIZED COUNTRY FOR PAYPLUG + if payment_provider.payment_type_payplug == True: + authorized_country_payplug=True + if country not in payment_provider.available_payplug_country_ids: + authorized_country_payplug=False + res.update({'authorized_country_payplug': authorized_country_payplug}) + # AUTHORIZED COUNTRY FOR AMEX + if payment_provider.payment_type_amex == True: + authorized_country_amex=True + if country not in payment_provider.available_amex_country_ids: + authorized_country_amex=False + res.update({'authorized_country_amex': authorized_country_amex}) + # AUTHORIZED COUNTRY FOR BANCONTACT + if payment_provider.payment_type_bancontact == True: + authorized_country_bancontact=True + if country not in payment_provider.available_bancontact_country_ids: + authorized_country_bancontact=False + res.update({'authorized_country_bancontact': authorized_country_bancontact}) + return res + + # UPDATE SESSION WITH PROVIDER PAYPLUG 'payplug','oney','amex','bancontact' AND 'oney_x3_with_fees','oney_x4_with_fees + @http.route(['/provider_payplug'], type='json', auth="public", website=True) + def provider_payplug(self, **kw): + payment_provider = kw.get('provider_payplug') + type_oney = kw.get('type_oney') + if payment_provider is None: + request.session['provider_payplug']='payplug' + else: + request.session['provider_payplug']=payment_provider + if type_oney is None: + request.session['type_oney']='oney_x3_with_fees' + else: + request.session['type_oney']=type_oney + +class PayPlugController(http.Controller): + _return_url = '/payment/payplug/return' + _webhook_url = '/payment/payplug/webhook' + + @http.route( + _return_url, type='http', auth='public', methods=['GET'], csrf=False, save_session=False + ) + def payplug_return_from_checkout(self, **post): + payment=request.env['payment.transaction'].sudo().browse(int(post.get('transaction'))) + # SECRET KEY TEST OR LIVE + secret_payplug_key=request.env['payment.provider'].sudo()._key_acquirer_state(payment.provider_id) + # API PAYPLUG + payplug.set_secret_key(secret_payplug_key) + payment_payplug=payplug.Payment.retrieve(str(payment.provider_reference)) + tx_sudo = request.env['payment.transaction'].sudo()._get_tx_from_notification_data('payplug', payment_payplug) + tx_sudo._handle_notification_data('payplug', payment_payplug) + return request.redirect('/payment/status') + + @http.route( + _webhook_url, type='http', auth='public', methods=['GET'], csrf=False + ) + def payplug_webhook(self, **post): + payment=request.env['payment.transaction'].sudo().browse(int(post.get('transaction'))) + # DOWNLOAD SECRET KEY PAYPLUG TEST OR LIVE + secret_payplug_key=request.env['payment.provider'].sudo()._key_acquirer_state(payment.provider_id) + # API PAYPLUG + payplug.set_secret_key(secret_payplug_key) + payment_payplug=payplug.Payment.retrieve(str(payment.provider_reference)) + # TRANSACTION CANCEL OR ERROR + CodeError=payment_payplug.failure.__dict__.get('_attributes') + if CodeError['code']=='canceled': + ErrorMessage='PayPlug Error\n' + ErrorMessage+='Transaction canceled by customer' + payment.sudo().write({ + 'state_message': ErrorMessage, + 'state': 'cancel', + }) + else: + ErrorMessage='PayPlug Error\n' + ErrorMessage+='Code Error : '+CodeError['code']+'\n' + ErrorMessage+='Message : '+CodeError['message'] + payment.sudo().write({ + 'state_message': ErrorMessage, + 'state': 'error', + }) + return request.redirect('/shop') + + \ No newline at end of file diff --git a/acquirer_payplug/controllers/portal.py b/acquirer_payplug/controllers/portal.py new file mode 100644 index 0000000..d225195 --- /dev/null +++ b/acquirer_payplug/controllers/portal.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- + +#from urllib.parse import urlparse, urljoin +from odoo import http +from odoo.http import request +from odoo import fields, http, tools, _ +from odoo.http import request +import logging +_logger = logging.getLogger(__name__) +import payplug +payplug.set_api_version("2019-08-06") +import pprint +from odoo.addons.sale.controllers.portal import CustomerPortal +from odoo.exceptions import AccessError, UserError, ValidationError + +class CustomerPortal(CustomerPortal): + + # FOR PAYMENT BY LINK + @http.route( + '/payment/pay', type='http', methods=['GET'], auth='public', website=True, sitemap=False, + ) + def payment_pay( + self, reference=None, amount=None, currency_id=None, partner_id=None, company_id=None, + provider_id=None, access_token=None, **kwargs): + res = super(CustomerPortal,self).payment_pay( + reference=reference, amount=amount, currency_id=currency_id, partner_id=partner_id, company_id=company_id, + provider_id=provider_id, access_token=access_token, **kwargs) + # IF PAYMENT ACQUIRER IS ONEY UPDATE VALUES + payment_acquirer=request.env['payment.provider'].sudo().search([('code','=','payplug')], limit=1) + partner = request.env['res.partner'].sudo().search([('id', '=', res.__dict__['qcontext']['partner_id'])]) + country = partner.country_id + if payment_acquirer.state != 'disabled': + if payment_acquirer.payment_type_oney == True: + amount = res.__dict__['qcontext']['amount'] + currency_symbol = res.__dict__['qcontext']['currency'].symbol + result_simulation=request.env['payment.provider'].sudo()._oney_payment_simulation(amount, 'FR') + res.__dict__['qcontext'].update(result_simulation) + # ONEY PAYMENT CONTROL BETWEEN €100 AND €3000 + if amount >= 100.00 and amount <= 3000.00: + authorized_by_oney = True + else: + authorized_by_oney = False + # AUTHORIZED COUNTRY FOR ONEY + authorized_country_oney=True + if country not in payment_acquirer.available_oney_country_ids: + authorized_country_oney=False + res.__dict__['qcontext'].update({ + 'InformationMessage': payment_acquirer.oney_information_message, + 'FooterMessage': payment_acquirer.oney_footer_message, + 'amount_total_oney': amount, + 'authorized_by_oney': authorized_by_oney, + 'acquirer': payment_acquirer, + 'currency_symbol': currency_symbol, + 'authorized_country_oney': authorized_country_oney, + }) + # AUTHORIZED COUNTRY FOR PAYPLUG + if payment_acquirer.payment_type_payplug == True: + authorized_country_payplug=True + if country not in payment_acquirer.available_payplug_country_ids: + authorized_country_payplug=False + res.__dict__['qcontext'].update({'authorized_country_payplug': authorized_country_payplug}) + # AUTHORIZED COUNTRY FOR AMEX + if payment_acquirer.payment_type_amex == True: + authorized_country_amex=True + if country not in payment_acquirer.available_amex_country_ids: + authorized_country_amex=False + res.__dict__['qcontext'].update({'authorized_country_amex': authorized_country_amex}) + # AUTHORIZED COUNTRY FOR BANCONTACT + if payment_acquirer.payment_type_bancontact == True: + authorized_country_bancontact=True + if country not in payment_acquirer.available_bancontact_country_ids: + authorized_country_bancontact=False + res.__dict__['qcontext'].update({'authorized_country_bancontact': authorized_country_bancontact}) + return res + + @http.route(['/my/orders/'], type='http', auth="public", website=True) + def portal_order_page(self, order_id, report_type=None, access_token=None, message=False, download=False, **kw): + res = super(CustomerPortal,self).portal_order_page(order_id, report_type, access_token, message, download, **kw) + if res.__dict__['qcontext'].get('sale_order'): + # IF PAYMENT ACQUIRER IS ONEY UPDATE VALUES + payment_provider=request.env['payment.provider'].sudo().search([('code','=','payplug')], limit=1) + country = res.__dict__['qcontext']['sale_order'].partner_invoice_id.country_id + if payment_provider.state != 'disabled': + if payment_provider.payment_type_oney == True: + amount = res.__dict__['qcontext']['sale_order'].amount_total + currency_symbol = res.__dict__['qcontext']['sale_order'].currency_id.symbol + result_simulation=request.env['payment.provider'].sudo()._oney_payment_simulation(amount, 'FR') + res.__dict__['qcontext'].update(result_simulation) + # ONEY PAYMENT CONTROL BETWEEN €100 AND €3000 + if amount >= 100.00 and amount <= 3000.00: + authorized_by_oney = True + else: + authorized_by_oney = False + # AUTHORIZED COUNTRY FOR ONEY + authorized_country_oney=True + if country not in payment_provider.available_oney_country_ids: + authorized_country_oney=False + res.__dict__['qcontext'].update({ + 'InformationMessage': payment_provider.oney_information_message, + 'FooterMessage': payment_provider.oney_footer_message, + 'amount_total_oney': amount, + 'authorized_by_oney': authorized_by_oney, + 'acquirer': payment_provider, + 'currency_symbol': currency_symbol, + 'authorized_country_oney': authorized_country_oney, + }) + # AUTHORIZED COUNTRY FOR PAYPLUG + if payment_provider.payment_type_payplug == True: + authorized_country_payplug=True + if country not in payment_provider.available_payplug_country_ids: + authorized_country_payplug=False + res.__dict__['qcontext'].update({'authorized_country_payplug': authorized_country_payplug}) + # AUTHORIZED COUNTRY FOR AMEX + if payment_provider.payment_type_amex == True: + authorized_country_amex=True + if country not in payment_provider.available_amex_country_ids: + authorized_country_amex=False + res.__dict__['qcontext'].update({'authorized_country_amex': authorized_country_amex}) + # AUTHORIZED COUNTRY FOR BANCONTACT + if payment_provider.payment_type_bancontact == True: + authorized_country_bancontact=True + if country not in payment_provider.available_bancontact_country_ids: + authorized_country_bancontact=False + res.__dict__['qcontext'].update({'authorized_country_bancontact': authorized_country_bancontact}) + return res diff --git a/acquirer_payplug/datas/neutralize.sql b/acquirer_payplug/datas/neutralize.sql new file mode 100644 index 0000000..735feaa --- /dev/null +++ b/acquirer_payplug/datas/neutralize.sql @@ -0,0 +1,4 @@ +-- disable payplug payment provider +UPDATE payment_provider + SET payplug_secret_test_key = NULL, + payplug_secret_live_key = NULL; diff --git a/acquirer_payplug/datas/payment_icon_data.xml b/acquirer_payplug/datas/payment_icon_data.xml new file mode 100644 index 0000000..75df3e4 --- /dev/null +++ b/acquirer_payplug/datas/payment_icon_data.xml @@ -0,0 +1,18 @@ + + + + VISA PayPlug + + + + + CB PayPlug + + + + + MASTERCARD PayPlug + + + + \ No newline at end of file diff --git a/acquirer_payplug/datas/payment_provider_data.xml b/acquirer_payplug/datas/payment_provider_data.xml new file mode 100644 index 0000000..d15b0b2 --- /dev/null +++ b/acquirer_payplug/datas/payment_provider_data.xml @@ -0,0 +1,40 @@ + + + + + + PayPlug Multi-Payments + + payplug + + Payment by credit card - PayPlug + + + Payment by credit card - PayPlug + Payment by credit card - PayLater 3x or 4x + Payment by credit card - American Express + Payment by credit card - Bancontact + + + + + + + +

You will be redirected to the PayPlug website after clicking on the payment button.

+
+ The amount of your order must be between € 100.00 and € 3,000.00 to pay with Oney. + y placing an order, you consent to our GTC. Financing supply with compulsory down payment, reserved for individuals and valid for any procurement from € 100.00 to € 3,000.00. Subject to acceptance by Oney Bank. You have a 14-day time to renounce your credit. Oney Bank - SA with capital of € 51,286,585 - 34 Avenue de Flandre 59170 Croix - 546 380 197 RCS Lille Métropole - No. Orias 07 023 261 www.orias.fr Correspondence: CS 60 006 - 59895 Lille Cedex - www.oney.fr +
+ + + +
\ No newline at end of file diff --git a/acquirer_payplug/i18n/fr.po b/acquirer_payplug/i18n/fr.po new file mode 100644 index 0000000..73cf309 --- /dev/null +++ b/acquirer_payplug/i18n/fr.po @@ -0,0 +1,614 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * acquirer_payplug +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0-20230130\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-02-08 05:55+0000\n" +"PO-Revision-Date: 2023-02-08 05:55+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.oney_detail_simulation +msgid "" +"(Cost of financing: 8,93 €TAEG : 19,26 €)" +msgstr "" +"(Coût du financement : 8,93 €TAEG : 19,26 €)" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.icon_list_oney +msgid "1st monthly payment:" +msgstr "1ére mensualité :" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.icon_list_oney +msgid "2nd monthly payment:" +msgstr "2ème mensualité :" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.icon_list_oney +msgid "3nd monthly payment:" +msgstr "3ème mensualité :" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.icon_list_oney +msgid "" +" Select 3x or 4x " +"payment." +msgstr "" +" Select 3x ou 4x " +"payment." + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" To use AmEx you must make an " +"activation request to PayPlug. This type of payment does not have a \"test\"" +" mode." +msgstr "" +" Pour utiliser AmEx vous devez " +"faire une demande d'activation auprès de PayPlug. Ce type de paiement n'a pas de mode " +" \"test\"." + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" To use Bancontact you " +"must make an activation request to PayPlug. This type of payment does not " +"have a \"test\" mode." +msgstr "" +" Pour utiliser Bancontact vous devez" +"faire une demande d'activation auprès de PayPlug. Ce type de paiement " +"n'a pas de mode \"test\"." + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" The configuration of " +"the authorized countries is to be carried out for each method of " +"payment!" +msgstr "" +" La configuration " +"des pays autorisés est à effectuer pour chaque moyen de " +"paiement !" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" PayPlug online payments via credit" +" cards American express." +msgstr "" +" PayPlug paiement en ligne par carte de crédit " +"merican express." + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" PayPlug online payments via " +"credit cards Bancontact" +msgstr "" +" PayPlug paiement en ligne par carte de crédit " +"Bancontact" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" PayPlug online payments" +" via credit cards." +msgstr "" +" PayPlug paiement en ligne par carte de crédit " +" PayPlug." + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +" PayLater online payments via " +"credit cards 3x or 4x." +msgstr "" +" PayLater paiement en ligne par carte de crédit 3x" +" ou 4x." + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.checkout_inherit +msgid " Delete" +msgstr "" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.oney_detail_simulation +msgid "3x payment" +msgstr "3x paiement" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.oney_detail_simulation +msgid "4x payment" +msgstr "4x paiement" + +#. module: acquirer_payplug +#: model_terms:ir.ui.view,arch_db:acquirer_payplug.payment_provider_form_inherit +msgid "" +"= 100 and amount <= 3000 and result.get('x3_with_fees'): + vals['payment_3x']={ + 'contribution_of_3x': "%.2f" % (int(result['x3_with_fees']['down_payment_amount'])/100), + '1st_monthly_payment_3x': "%.2f" % (int(result['x3_with_fees']['installments'][0]['amount'])/100), + '2nd_monthly_payment_3x': "%.2f" % (int(result['x3_with_fees']['installments'][1]['amount'])/100), + 'total_payment_3x': "%.2f" % (int(result['x3_with_fees']['down_payment_amount'])/100 \ + +int(result['x3_with_fees']['installments'][0]['amount'])/100 \ + +int(result['x3_with_fees']['installments'][1]['amount'])/100), + } + vals['payment_4x']={ + 'contribution_of_4x': "%.2f" % (int(result['x4_with_fees']['down_payment_amount'])/100), + '1st_monthly_payment_4x': "%.2f" % (int(result['x4_with_fees']['installments'][0]['amount'])/100), + '2nd_monthly_payment_4x': "%.2f" % (int(result['x4_with_fees']['installments'][1]['amount'])/100), + '3nd_monthly_payment_4x': "%.2f" % (int(result['x4_with_fees']['installments'][2]['amount'])/100), + 'total_payment_4x': "%.2f" % (int(result['x4_with_fees']['down_payment_amount'])/100 \ + + int(result['x4_with_fees']['installments'][0]['amount'])/100 \ + + int(result['x4_with_fees']['installments'][1]['amount'])/100 \ + + int(result['x4_with_fees']['installments'][2]['amount'])/100), + } + else: + vals['payment_3x']={ + 'contribution_of_3x': "0.00", + '1st_monthly_payment_3x': "0.00", + '2nd_monthly_payment_3x': "0.00", + 'total_payment_3x': "0.00", + } + vals['payment_4x']={ + 'contribution_of_4x': "0.00", + '1st_monthly_payment_4x': "0.00", + '2nd_monthly_payment_4x': "0.00", + '3nd_monthly_payment_4x': "0.00", + 'total_payment_4x': "0.00", + } + return vals + + # RETURN TEST KEY OR LIVE KEY + def _key_acquirer_state(self, acquirer): + if acquirer.code == 'payplug' and acquirer.code != 'disabled': + if acquirer.state == 'test': + secret_payplug_key = acquirer.payplug_secret_test_key + else: + secret_payplug_key = acquirer.payplug_secret_live_key + return secret_payplug_key + + code = fields.Selection( + selection_add=[('payplug', "PayPlug")], ondelete={'payplug': 'set default'}) + payplug_secret_test_key = fields.Char( + string="Secret Test Key", + help="Enter the TEST key of your PayPlug account", + required_if_provider='payplug', + groups='base.group_user') + payplug_secret_live_key = fields.Char( + string="Secret Live Key", + help="Enter the LIVE key of your PayPlug account", + required_if_provider='payplug', + groups='base.group_user') + # PAYPLUG VALUES + payplug_display_as = fields.Char( + string="PayPlug displayed as", help="Description of the acquirer for customers", + translate=True) + payment_type_payplug = fields.Boolean("Payment PayPlug", default=True) + available_payplug_country_ids = fields.Many2many( + string="Payplug countries", comodel_name='res.country', relation='payplug_payment_country_rel', + column1='payment_id', column2='country_id', + help="The countries for which this payment acquirer is available.\n" + "If none is set, it is available for all countries.") + # AMEX VALUES + amex_display_as = fields.Char( + string="Amex displayed as", help="Description of the acquirer for customers", + translate=True) + payment_type_amex = fields.Boolean("Payment AmEx") + available_amex_country_ids = fields.Many2many( + string="Amex countries", comodel_name='res.country', relation='amex_payment_country_rel', + column1='payment_id', column2='country_id', + help="The countries for which this payment acquirer is available.\n" + "If none is set, it is available for all countries.") + # BANCONTACT VALUES + bancontact_display_as = fields.Char( + string="Bancontact displayed as", help="Description of the acquirer for customers", + translate=True) + payment_type_bancontact = fields.Boolean("Payment Bancontact") + available_bancontact_country_ids = fields.Many2many( + string="Bancontact countries", comodel_name='res.country', relation='bancontact_payment_country_rel', + column1='payment_id', column2='country_id', + help="The countries for which this payment acquirer is available.\n" + "If none is set, it is available for all countries.") + # ONEY VALUES + oney_display_as = fields.Char( + string="PayLater displayed as", help="Description of the acquirer for customers", + translate=True) + payment_type_oney = fields.Boolean("Payment PayLater") + available_oney_country_ids = fields.Many2many( + string="Paylater countries", comodel_name='res.country', relation='oney_payment_country_rel', + column1='payment_id', column2='country_id', + help="The countries for which this payment acquirer is available.\n" + "If none is set, it is available for all countries.") + oney_information_message = fields.Char( + 'Information message', + translate=True, + required_if_provider='oney', + groups='base.group_user') + oney_footer_message = fields.Text( + 'Footer pop-up message', + translate=True, + required_if_provider='oney', + groups='base.group_user') + + # CREATE DIGITAL KEY + def _playplug_generate_digital_sign(self, values): + keys = "reference customer_name customer_postcode".split() + def get_value(key): + if values.get(key): + return values[key] + return '' + values = dict(values or {}) + sign = ''.join('%s=%s' % (k, get_value(k)) for k in keys) + shasign = sha1(sign.encode('utf-8')).hexdigest() + return shasign + + + + + + diff --git a/acquirer_payplug/models/payment_transaction.py b/acquirer_payplug/models/payment_transaction.py new file mode 100644 index 0000000..49cb888 --- /dev/null +++ b/acquirer_payplug/models/payment_transaction.py @@ -0,0 +1,271 @@ +# coding: utf-8 +#from hashlib import sha1 +from urllib.parse import urlparse, urljoin +from odoo import api, fields, tools, models, _ +from odoo.addons.acquirer_payplug.controllers.main import PayPlugController +from odoo.http import request +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT +from datetime import datetime, timedelta +from odoo.exceptions import AccessError, UserError, ValidationError +import logging +_logger = logging.getLogger(__name__) +import pprint +import payplug +payplug.set_api_version("2019-08-06") + +class TxPayPlug(models.Model): + _inherit = 'payment.transaction' + + provider_type_payplug = fields.Selection([ + ('payplug', 'PayPlug'), + ('oney', 'Paylater by Oney'), + ('amex', 'Americam Express'), + ('bancontact', 'Bancontact'), + ], string="Acquirer Payplug") + + # TO EXTRACT THE NAME AND FIRST NAME FROM THE "NAME" FIELD + def _partner_values(self, partner): + vals=[] + if partner: + if len(partner.name.split(' ')) == 1: + vals.append(partner.name) + vals.append('None') + else: + vals=partner.name.split(' ',1) + return vals + + # CREATE SPECIFIC VALUES FOR ONEY + def _order_lines_oney(self, order): + shop_lines=[] + if order: + for line in order.order_line: + shop_lines.append({ + 'expected_delivery_date': (datetime.now()+timedelta(days=2)).strftime(DEFAULT_SERVER_DATE_FORMAT), + 'delivery_label': order.company_id.name, + 'brand': order.company_id.name, + 'delivery_type': 'carrier', + 'merchant_item_id': 'REF-'+str(line.id), + 'name': line.product_id.name, + 'total_amount': int(line.price_total*100), + 'price': int(line.price_unit+(line.price_tax/line.product_uom_qty)*100), + 'quantity': int(line.product_uom_qty), + }) + return shop_lines + + def _get_specific_rendering_values(self, processing_values): + tx_values = super()._get_specific_rendering_values(processing_values) + # IF NOT PROVIDER PAYPLUG + if self.provider_code != 'payplug': + return tx_values + # DOWNLOAD SECRET KEY PAYPLUG TEST OR LIVE + secret_payplug_key = request.env['payment.provider']._key_acquirer_state(self.provider_id) + # BASE URL FOR RETURN + base_url = self.provider_id.get_base_url() + # SECRET KEY TEST OR LIVE + payplug.set_secret_key(secret_payplug_key) + + # PROCESSING TO EXTRACT THE ORDER NUMBER WITHOUT SO000-1, SO000-2 ADD IF SEVERAL PAYMENT ATTEMPTS + if self.reference: + order_id = self.reference.split('-',1)[0] + # FROM ORDERS + order = request.env['sale.order'].sudo().search([('name', '=', order_id)]) + type_of_request = 'sale.order' + partner_invoice = order.partner_invoice_id + if not order: + # FROM INVOICE + order = request.env['account.move'].sudo().search([('name', '=', self.reference)]) + type_of_request = 'account.move' + partner_invoice = order.partner_id + + # MANDATORY VALUE CONTROL + if not order.partner_shipping_id.country_id or not order.partner_invoice_id.country_id : + raise ValidationError("PayPlug: " + _("COUNTRY is required!")) + if not order.partner_shipping_id.city or not order.partner_invoice_id.city : + raise ValidationError("PayPlug: " + _("CITY is required!")) + if not order.partner_shipping_id.zip or not order.partner_invoice_id.zip : + raise ValidationError("PayPlug: " + _("ZIP is required!")) + if not order.partner_shipping_id.phone or not order.partner_invoice_id.phone : + raise ValidationError("PayPlug: " + _("PHONE is required!")) + if not order.partner_shipping_id.email or not order.partner_invoice_id.email : + raise ValidationError("PayPlug: " + _("EMAIL is required!")) + + # GENERATE DIGITAL KEY + payment_generate_key = { + 'reference': order.name, + 'customer_name': order.partner_id.name, + 'customer_postcode': order.partner_id.zip, + } + digital_Key = self.env['payment.provider']._playplug_generate_digital_sign(payment_generate_key) + partner_billing=self._partner_values(order.partner_invoice_id) + partner_shipping=self._partner_values(order.partner_shipping_id) + # FORMAT INTERNATIONNAL PHONE BILLING + if order.partner_invoice_id.phone: + BillingPhoneFormat=request.env['res.partner'].sudo()._phone_format( + partner_invoice.phone or order.partner_id.phone, + partner_invoice.country_id, + order.company_id) + if BillingPhoneFormat: + BillingPhoneFormat=BillingPhoneFormat.replace(' ','') + # FORMAT INTERNATIONNAL PHONE SHIPPING + if order.partner_shipping_id.phone: + ShippingPhoneFormat=request.env['res.partner'].sudo()._phone_format( + order.partner_shipping_id.phone or order.partner_id.phone, + order.partner_shipping_id.country_id, + order.company_id) + if ShippingPhoneFormat: + ShippingPhoneFormat=ShippingPhoneFormat.replace(' ','') + # BILLING FULL ADDRESS + billing_full_address = order.partner_id.street + if partner_invoice.street2: + billing_full_address += ' - '+partner_invoice.street2 + # SHIPPING FULL ADDRESS + shipping_full_address = order.partner_shipping_id.street + if order.partner_shipping_id.street2: + shipping_full_address += ' - '+order.partner_shipping_id.street2 + payment_data = {} + payment_data = { + # AMOUNT DIFFERENT BY PAYPLUG PROVIDER + 'currency': str(order.currency_id.name), + 'billing': { + 'first_name': partner_billing[0], + 'last_name': partner_billing[1], + 'email': partner_invoice.email, + 'address1': billing_full_address, + 'postcode': partner_invoice.zip, + 'city': partner_invoice.city, + 'country': partner_invoice.country_id.code, + 'language': partner_invoice.country_id.code.lower(), + }, + 'shipping': { + 'first_name': partner_shipping[0], + 'last_name': partner_shipping[0], + 'email': order.partner_shipping_id.email or order.partner_id.email, + 'address1': shipping_full_address, + 'postcode': order.partner_shipping_id.zip, + 'city': order.partner_shipping_id.city, + 'country': order.partner_shipping_id.country_id.code, + 'language': order.partner_shipping_id.country_id.code.lower(), + 'delivery_type': 'BILLING' + }, + 'hosted_payment': { + 'return_url': '%s' % urljoin(base_url, PayPlugController._return_url+'?transaction='+str(self.id)), + 'cancel_url': '%s' % urljoin(base_url, PayPlugController._webhook_url+'?transaction='+str(self.id)), + }, + 'metadata': { + 'DigitalKey': digital_Key, + 'return_validate_url': '', + }, + } + # TO DEFINE THE METHOD ACCORDING TO THE PAYPLUG PROVIDER AND SPECIFIC VALUES + payment_method = request.session.get('provider_payplug') + payment_method_oney = request.session.get('type_oney') + amount_order = int(processing_values.get('amount')*100) + if payment_method == 'payplug': + payment_method_type = 'payplug' + payment_data['amount'] = amount_order + payment_data['metadata']['payment_method'] = 'payplug' + if payment_method == 'oney': + payment_method_type = 'oney' + payment_data['metadata']['payment_method'] = 'oney' + payment_data['authorized_amount'] = amount_order + payment_data['payment_method'] = payment_method_oney + payment_data['billing']['mobile_phone_number'] = BillingPhoneFormat + payment_data['shipping']['mobile_phone_number'] = ShippingPhoneFormat + payment_data['shipping']['company_name'] = partner_invoice.parent_id.name or partner_invoice.company_id.name + payment_data['payment_context'] = {} + payment_data['payment_context']['cart'] = self._order_lines_oney(order) + if payment_method == 'american_express': + payment_method_type = 'amex' + payment_data['amount'] = amount_order + payment_data['payment_method'] = 'american_express' + payment_data['shipping']['company_name'] = partner_invoice.parent_id.name or partner_invoice.company_id.name + payment_data['metadata']['payment_method'] = 'amex' + if payment_method == 'bancontact': + payment_method_type = 'bancontact' + payment_data['amount'] = amount_order + payment_data['payment_method'] = 'bancontact' + payment_data['shipping']['company_name'] = partner_invoice.parent_id.name or partner_invoice.company_id.name + payment_data['metadata']['payment_method'] = 'bancontact' + payplug_tx_values = {} + if processing_values.get('reference') != '/': + # CREATE PAYMENT + payment = payplug.Payment.create(**payment_data) + payment_id = str(payment.id) + # RETURN URL PAYPLUG FOR PAYMENT + payplug_tx_values = dict(processing_values) + if self.provider_code == 'payplug': + payplug_tx_values.update({ + 'payplug_url': '%s' % payment.hosted_payment.payment_url, + }) + # WRITE REFERENCE SUPPLIER IN PAYMENT TRANSACTION + if processing_values.get('reference') != '/': + transaction_id = request.env['payment.transaction'].sudo().search([('reference', '=', str(processing_values.get('reference')))], limit=1) + transaction_id.sudo().write({ + 'provider_reference': payment_id, + 'provider_type_payplug': payment_method_type}) + return payplug_tx_values + + # RETOUR A PARTIR DU CONTROLEUR + @api.model + def _get_tx_from_notification_data(self, provider, Payment): + tx = key = False + tx = super()._get_tx_from_notification_data(provider, Payment) + if provider != 'payplug' or len(tx) == 1: + return tx + if isinstance(Payment,object) == True: + tx = self.search([('provider_reference', '=', str(Payment.id))], limit=1) + if tx: + vals = { + 'reference': tx.reference.split('-')[0], + 'customer_name': tx.partner_id.name, + 'customer_postcode': tx.partner_id.zip, + } + # CREATE NEW DIGITAL KEY FOR CONTROL + key = Payment.metadata.get('DigitalKey') + control_digital_key = tx.provider_id._playplug_generate_digital_sign(vals) + if key.upper() != control_digital_key.upper(): + raise ValidationError( + "PayPlug: " + _( + "Invalid Key: received %(sign)s, computed %(check)s", + sign=key.upper(), check=control_digital_key.upper() + ) + ) + return tx + + def _process_notification_data(self, notification_data): + super()._process_notification_data(notification_data) + if self.provider_code != 'payplug': + return + # SPECIFIC STATE ONEY + if notification_data.metadata.get('payment_method') == 'oney': + if notification_data.authorization and notification_data.authorization.get('authorized_at'): + # ACCEPTED BY ONEY + self.sudo().write({'provider_reference': self.provider_reference}) + self._set_done(_("Your payment %s has been validated by Oney.", notification_data.payment_method.get('type'))) + return True + if notification_data.is_paid == False and notification_data.failure == None: + # WAITING FOR VALIDATION BY ONEY + self.sudo().write({'state_message': 'Oney: awaiting approval'}) + self._set_pending(_("Your payment %s has been successfully processed but is awaiting approval by Oney.", notification_data.payment_method.get('type'))) + return None + if notification_data.is_paid == False and notification_data.failure != None: + # REFUSE BY ONEY + self.sudo().write({'state_message': 'Oney: feedback error'}) + self._set_error(_("Your payment in %s was refused by Oney.", notification_data.payment_method.get('type'))) + # OTHER PROVIDER PAYPLUG + else: + if notification_data.is_paid == True and notification_data.failure == None: + # ACCEPTED + self.sudo().write({'provider_reference': self.provider_reference}) + self._set_done() + return True + if notification_data.is_paid == False and notification_data.failure == None: + # WAITING FOR VALIDATION + self.sudo().write({'state_message': 'PayPlug: feedback error'}) + self._set_pending() + return None + if notification_data.is_paid == False and notification_data.failure != None: + # REFUSE + self.sudo().write({'state_message': 'PayPlug: feedback error'}) + self._set_error(_("Your payment was refused.")) + return False \ No newline at end of file diff --git a/acquirer_payplug/static/.DS_Store b/acquirer_payplug/static/.DS_Store new file mode 100644 index 0000000..5693cf8 Binary files /dev/null and b/acquirer_payplug/static/.DS_Store differ diff --git a/acquirer_payplug/static/description/MOCKUP_PAIEMENT_EN.png b/acquirer_payplug/static/description/MOCKUP_PAIEMENT_EN.png new file mode 100644 index 0000000..3cf634b Binary files /dev/null and b/acquirer_payplug/static/description/MOCKUP_PAIEMENT_EN.png differ diff --git a/acquirer_payplug/static/description/banner.png b/acquirer_payplug/static/description/banner.png new file mode 100644 index 0000000..fbda489 Binary files /dev/null and b/acquirer_payplug/static/description/banner.png differ diff --git a/acquirer_payplug/static/description/icon.png b/acquirer_payplug/static/description/icon.png new file mode 100644 index 0000000..9c8ef4b Binary files /dev/null and b/acquirer_payplug/static/description/icon.png differ diff --git a/acquirer_payplug/static/description/index.html b/acquirer_payplug/static/description/index.html new file mode 100644 index 0000000..81be281 --- /dev/null +++ b/acquirer_payplug/static/description/index.html @@ -0,0 +1,161 @@ +
+
+
+

About PayPlug

+
+
+
+ +
+
+
+
+ PayPlug is a french omnichannel solution dedicated to SMEs. It enables merchants to accept card payments: +
+
    +
  • from online shops;
  • +
  • from physical stores;
  • +
  • through conversations (WhatsApp, Messenger, SMS, e-mail) thanks to a payment requests tool available from the PayPlug portal;
  • +
  • or directly from an invoice or a quote thanks to a payment links generation tool.
  • +
  • Boost your conversion rate, your average shopping cart value and your turnover by offering your customers the possibility to pay their orders in 3 or 4 installments without any risk for your cash flow.
  • + +
+
+
+
+ +
+
+
+

Odoo & PayPlug features

+
+
+
+ +
+
+
+
+ Accept card payments from your Odoo shop: +
+
    +
  • + Install our module in a few clicks
  • +
  • + Safely accept Visa, MasterCard, American Express, Bancontact and debit cards;
  • +
  • + Enjoy a personalized redirected payment page (logo, back screen image, page colors).
  • +
  • + Enable to pay in 3 or 4 installments:
  • +   - A simple and secure shopping experience with no receipts and with an instant response.
    +   - Future purchases made easier thanks to the 1-click payment with the Oney account.
    +   - Up to 98% payment acceptance rate for your customers. +
+
+
+ +
+
+
+ Efficiently monitor your performance: +
+
    +
  • Follow your transactions in real time;
  • +
  • Discover the reasons for cancelled and failed payments;
  • +
  • Download your accounting exports in a few clicks from your PayPlug portal.
  • + +
  • Buy Now and Paylater with secured cash flow:
  • +   - Full coverage for fraud and outstanding payments by Oney.
    +   - Recovery of funds within 24 hours on your bank account.
    +   - Eligibility conditions and transparent pricing. +
+
+ +
+
+
+ +
+
+
+ Accept payments thanks to your invoices: +
+
    +
  • When sending your customers their invoices or quotes, slip in a payment link, generated from your PayPlug portal;
  • +
  • Your customers will pay their invoices or quotes both safely and easily by card. They will love it;
  • +
  • From your side : say bye to late payments!.
  • +
+
+
+
+

The PayPlug team is located in Paris and will be happy to assist you in your preferred language (french, english, italian).


+
+
+
+
+
+
+
+ Technical parameters: +
+
    +
  • + You must install the PayPlug API: sudo pip3 install --upgrade payplug==1.3.1
  • +
  • + In odoo.sh: Create a requirements.txt file on your default branch and add the line: payplug==1.3.1
  • +
  • + Documentation: www.payplug.com


  • +
  • + Contact US:
    Email: support@applylog.com
    Website: www.applylog.com
  • + +
+
+
+
+ +

+ + + + + + + + + + + + + + + + + + + + diff --git a/acquirer_payplug/static/src/css/website_oney.css b/acquirer_payplug/static/src/css/website_oney.css new file mode 100644 index 0000000..84ae5f6 --- /dev/null +++ b/acquirer_payplug/static/src/css/website_oney.css @@ -0,0 +1,271 @@ +.oney_none_information { + color: gray; + border: 1px solid gray; + width: 50px; +} + +.table_x_oney { + font-size: 12px; + color: gray; +} + +.banner-oney-product { + width: 135px; + position: relative; + display: inline-block; +} + +.banner-oney-product .img-oney { + width: 135px; +} + +.banner-oney-product .img-oney-top { + width: 135px; + display: none; + position: absolute; + top: 0; + left: 0; + z-index: 200; +} + +.banner-oney-product:hover .img-oney-top { + display: inline; +} + +.div-oney-none { + width: 300px; + min-height: 80px; + display: table; + position: absolute; + background: white; + border: 3px solid #cdcecc; +} + +.arrow-left-none { + border: 3px solid #cdcecc; + border-width: 0 3px 3px 0; + display: inline-block; + background-color: white; + padding: 8px; + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + position: relative; + left: 287px; + top: -30px; +} + +.div-oney-none-text { + position: relative; + display: block; + width: 97%; + height: 100%; + margin: 4px; + color: #b3b3b3; + font-style: italic; +} + +.div-oney { + width: 352px; + height: 464px; + position: absolute; + border: 2px solid #81bc00; + display: table; + background: white; + z-index: 1022; +} + +.arrow-left { + border: 2px solid #81bc00; + border-width: 0 3px 3px 0; + display: inline-block; + background-color: white; + padding: 8px; + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + position: relative; + left: 341px; + top: 150px; +} + +.div2-oney { + top: -23px; + height: 97%; + display: block; + position: relative; + margin: 5px; + background: white; +} + +.div2-oney .div-oney-close { + text-align: right; + height: 20px; +} + +.div-oney-close .oney-close { + font-size: 20px; + font-family: Arial, Helvetica, sans-serif; + display: relative; + color: #81bc00; + font-weight: bold; + text-align: right; + text-decoration: none; +} + +.div-oney .div2-oney .title_oney { + width: 62%; + margin-bottom: 3px; + display: block; +} + +.div2-oney .footer-oney { + font-family: Arial, Helvetica, sans-serif; + margin-top: 6px; + font-size: 10px; + color: gray; + text-align: justify; +} + +/* BUTTON 3x ONEY */ +.div2-oney .button-3x-group { + height: 25px; + border-top: 2px solid #cdcecc; + border-left: 2px solid #cdcecc; + border-right: 2px solid #cdcecc; +} + +.button-3x-group .text-button { + color: #777; + font-family: Arial, Helvetica, sans-serif; + margin-left: 8px; + font-size: 15px; +} + +.button-3x-group:hover { + background: #e7fad9; + font-family: Arial, Helvetica, sans-serif; + border-top: 2px solid #81bc00; + border-left: 2px solid #81bc00; + border-right: 2px solid #81bc00; +} + +.button-3x-group-active { + background: #e7fad9; + height: 27px; + font-family: Arial, Helvetica, sans-serif; + border: 2px solid #81bc00; +} + +.button-3x-group-active .text-button { + color: #777; + font-family: Arial, Helvetica, sans-serif; + font-size: 15px; + margin-left: 8px; +} + +/* BUTTON 4x ONEY */ +.div2-oney .button-4x-group { + height: 25px; + border-bottom: 2px solid #cdcecc; + border-left: 2px solid #cdcecc; + border-right: 2px solid #cdcecc; +} + +.button-4x-group .text-button { + color: #777; + font-family: Arial, Helvetica, sans-serif; + margin-left: 8px; + font-size: 15px; +} + +.button-4x-group:hover { + background: #e7fad9; + font-family: Arial, Helvetica, sans-serif; + border-bottom: 2px solid #81bc00; + border-left: 2px solid #81bc00; + border-right: 2px solid #81bc00; +} + +.button-4x-group-active { + background: #e7fad9; + height: 27px; + font-family: Arial, Helvetica, sans-serif; + border: 2px solid #81bc00; +} + +.button-4x-group-active .text-button { + color: #777; + font-family: Arial, Helvetica, sans-serif; + margin-left: 8px; + font-size: 15px; +} + +.div-oney .div2-oney .payment-detail { + top: 5px; + font-family: Arial, Helvetica, sans-serif; + font-size: 13px; + position: relative; + font-family: Arial, Helvetica, sans-serif; + min-height: 159px; + color: gray; + border-bottom: 1px solid #81bc00; +} + +.div-oney .div2-oney .payment-detail .small-text { + font-family: Arial, Helvetica, sans-serif; + font-size: 10px; + color: gray; +} + +.div-oney .div2-oney .payment-detail .table-detail { + width: 100%; + border-spacing: 0px; +} + +.div-payment { + border-top: 2px solid #dde1e6; + position: relative; + top: -12px; +} + +.arrow-payment { + border: 2px solid #dde1e6; + border-width: 0 2px 2px 0; + display: inline-block; + background-color: white; + padding: 4px; + transform: rotate(-135deg); + -webkit-transform: rotate(-135deg); + position: relative; + height: 5px; + left: 20px; +} + +.text-right-simulation { + text-size: 10px; + text-align: right; + width: 90%; + background: transparent; + border: none; +} + +.text-price-product-oney { + margin-right: -18px; + text-align: right; + width: 40%; + background: transparent; + border: none; + font-weight: bold; +} + + + + + + + + + + + + + diff --git a/acquirer_payplug/static/src/img/icon/american_express.png b/acquirer_payplug/static/src/img/icon/american_express.png new file mode 100644 index 0000000..9f31d21 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/american_express.png differ diff --git a/acquirer_payplug/static/src/img/icon/amex/payment_amex_icon.png b/acquirer_payplug/static/src/img/icon/amex/payment_amex_icon.png new file mode 100644 index 0000000..9c8ef4b Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/amex/payment_amex_icon.png differ diff --git a/acquirer_payplug/static/src/img/icon/bancontact.png b/acquirer_payplug/static/src/img/icon/bancontact.png new file mode 100644 index 0000000..19831e0 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/bancontact.png differ diff --git a/acquirer_payplug/static/src/img/icon/icon-cb.png b/acquirer_payplug/static/src/img/icon/icon-cb.png new file mode 100644 index 0000000..196005d Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/icon-cb.png differ diff --git a/acquirer_payplug/static/src/img/icon/icon-mastercard.png b/acquirer_payplug/static/src/img/icon/icon-mastercard.png new file mode 100644 index 0000000..21d3b22 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/icon-mastercard.png differ diff --git a/acquirer_payplug/static/src/img/icon/icon-visa.png b/acquirer_payplug/static/src/img/icon/icon-visa.png new file mode 100644 index 0000000..1017850 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/icon-visa.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/3x-gray.png b/acquirer_payplug/static/src/img/icon/oney/3x-gray.png new file mode 100644 index 0000000..c9879e5 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/3x-gray.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/3x-green.png b/acquirer_payplug/static/src/img/icon/oney/3x-green.png new file mode 100644 index 0000000..ac168fe Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/3x-green.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/4x-gray.png b/acquirer_payplug/static/src/img/icon/oney/4x-gray.png new file mode 100644 index 0000000..580737f Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/4x-gray.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/4x-green.png b/acquirer_payplug/static/src/img/icon/oney/4x-green.png new file mode 100644 index 0000000..30a4397 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/4x-green.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_1.png b/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_1.png new file mode 100644 index 0000000..5bc8fa7 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_1.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_2.png b/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_2.png new file mode 100644 index 0000000..a7ecdc0 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_2.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_3.png b/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_3.png new file mode 100644 index 0000000..5ac6af2 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/icon_oney_3x4x_3.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/logo-oney-3xpayant-gray.png b/acquirer_payplug/static/src/img/icon/oney/logo-oney-3xpayant-gray.png new file mode 100644 index 0000000..7e2170e Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/logo-oney-3xpayant-gray.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/logo-oney-3xpayant.png b/acquirer_payplug/static/src/img/icon/oney/logo-oney-3xpayant.png new file mode 100644 index 0000000..e0d7895 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/logo-oney-3xpayant.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/logo-oney-4xpayant-gray.png b/acquirer_payplug/static/src/img/icon/oney/logo-oney-4xpayant-gray.png new file mode 100644 index 0000000..c6f8f4a Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/logo-oney-4xpayant-gray.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/logo-oney-4xpayant.png b/acquirer_payplug/static/src/img/icon/oney/logo-oney-4xpayant.png new file mode 100644 index 0000000..77e51fd Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/logo-oney-4xpayant.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/payment_oney_icon.png b/acquirer_payplug/static/src/img/icon/oney/payment_oney_icon.png new file mode 100644 index 0000000..9c8ef4b Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/payment_oney_icon.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/popin-pedagogique.png b/acquirer_payplug/static/src/img/icon/oney/popin-pedagogique.png new file mode 100644 index 0000000..a02350a Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/popin-pedagogique.png differ diff --git a/acquirer_payplug/static/src/img/icon/oney/spinner1.gif b/acquirer_payplug/static/src/img/icon/oney/spinner1.gif new file mode 100644 index 0000000..ef0650a Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/spinner1.gif differ diff --git a/acquirer_payplug/static/src/img/icon/oney/spinner2.gif b/acquirer_payplug/static/src/img/icon/oney/spinner2.gif new file mode 100644 index 0000000..b65e941 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/spinner2.gif differ diff --git a/acquirer_payplug/static/src/img/icon/oney/title_popup.png b/acquirer_payplug/static/src/img/icon/oney/title_popup.png new file mode 100644 index 0000000..50998d8 Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/oney/title_popup.png differ diff --git a/acquirer_payplug/static/src/img/icon/payplug/button_logo.png b/acquirer_payplug/static/src/img/icon/payplug/button_logo.png new file mode 100644 index 0000000..9150c1f Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/payplug/button_logo.png differ diff --git a/acquirer_payplug/static/src/img/icon/payplug/payment_payplug_icon.png b/acquirer_payplug/static/src/img/icon/payplug/payment_payplug_icon.png new file mode 100644 index 0000000..9c8ef4b Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/payplug/payment_payplug_icon.png differ diff --git a/acquirer_payplug/static/src/img/icon/payplug/payplug_icon.png b/acquirer_payplug/static/src/img/icon/payplug/payplug_icon.png new file mode 100644 index 0000000..8af1edd Binary files /dev/null and b/acquirer_payplug/static/src/img/icon/payplug/payplug_icon.png differ diff --git a/acquirer_payplug/static/src/js/website_oney.js b/acquirer_payplug/static/src/js/website_oney.js new file mode 100644 index 0000000..bd79e07 --- /dev/null +++ b/acquirer_payplug/static/src/js/website_oney.js @@ -0,0 +1,199 @@ +odoo.define('acquirer_payplug.website_oney', function (require) { + "use strict"; + +var publicWidget = require('web.public.widget'); +var core = require('web.core'); +var _t = core._t; +var ajax = require('web.ajax'); +var timeout; + +publicWidget.registry.websiteSaleOney = publicWidget.Widget.extend({ + selector: '.oe_website_sale, .o_portal_wrap, form[name="o_payment_checkout"]', + events: { + 'mouseover a.js_oney_banner': '_onMouseOverOney', + 'mouseout a.js_oney_banner': '_onMouseOutOney', + 'click a.js_oney_banner': '_onClickBannerOney', + 'click a.js_oney_close': '_onClickClosePopupInformation', + 'click a.js_oney_banner_cart': '_onClickBannerCartOney', + + 'mouseover a.js_information_none_oney_cart': '_onMouseOverInformationNoneCart', + 'mouseout a.js_information_none_oney_cart': '_onMouseOutInformationNoneCart', + + 'click a.js_payment_img-3x-gray': '_onClickPaymentImage', + 'click a.js_payment_img-4x-gray': '_onClickPaymentImage', + 'mouseover a.js_oney_information': '_onMouseOverOneyInformation', + 'mouseout a.js_oney_information': '_onMouseOutOneyInformation', + 'mouseover a.js_oney_information_payment': '_onMouseOverOneyInformationPayment', + 'mouseout a.js_oney_information_payment': '_onMouseOutOneyInformationPayment', + 'click .o_provider_payplug_card' : '_onClickBannerProvider', + }, + + init: function () { + this._super.apply(this, arguments); + }, + + start: function () { + return this._super.apply(this, arguments); + }, + + // Close pop-up information cart + _onClickClosePopupInformation: function(ev) { + document.getElementById('oney_popup_card').style.display = 'none'; + }, + + // Over information Inline in cart + _onMouseOverInformationNoneCart: function (ev) { + document.getElementById('div_oney_none').style.display = 'inline'; + }, + + // Out information None in cart + _onMouseOutInformationNoneCart: function (ev) { + document.getElementById('div_oney_none').style.display = 'none'; + }, + + _onClickBannerCartOney: function (ev) { + // CALL WITH PYTHON JSON RPC FOR GET SIMULATION + ajax.jsonRpc('/shop/product/oney_simulation', 'call', { + 'product_total_price': document.getElementById('total_price_cart').value, + }).then(function (data) { + document.getElementById('amount_total_oney_3x').value = data['amount_total_oney']; + document.getElementById('contribution_of_3x').value = data['payment_3x']['contribution_of_3x']; + document.getElementById('1st_monthly_payment_3x').value = data['payment_3x']['1st_monthly_payment_3x']; + document.getElementById('2nd_monthly_payment_3x').value = data['payment_3x']['2nd_monthly_payment_3x']; + document.getElementById('total_payment_3x').value = data['payment_3x']['total_payment_3x']; + document.getElementById('amount_total_oney_4x').value = data['amount_total_oney']; + document.getElementById('contribution_of_4x').value = data['payment_4x']['contribution_of_4x']; + document.getElementById('1st_monthly_payment_4x').value = data['payment_4x']['1st_monthly_payment_4x']; + document.getElementById('2nd_monthly_payment_4x').value = data['payment_4x']['2nd_monthly_payment_4x']; + document.getElementById('3nd_monthly_payment_4x').value = data['payment_4x']['3nd_monthly_payment_4x']; + document.getElementById('total_payment_4x').value = data['payment_4x']['total_payment_4x']; + }); + document.getElementById('oney_popup_card').style.display = 'inline'; + }, + + // Change img Oney Over + _onMouseOverOney: function (ev) { + document.getElementById('img-oney').style.display = 'none'; + document.getElementById('img-oney-top').style.display = 'inline'; + }, + + // Change img Oney Out + _onMouseOutOney: function (ev) { + document.getElementById('img-oney').style.display = 'inline'; + document.getElementById('img-oney-top').style.display = 'none'; + }, + + _onMouseOverOneyInformation: function (ev) { + var element = document.getElementById('img-oney-gray'); + var position = element.getBoundingClientRect(); + var element1 = document.getElementById('InformationOney'); + var position1 = element1.getBoundingClientRect(); + document.getElementById('div_oney_information_none').style.display = 'inline'; + document.getElementById('div_oney_information_none').style.left = '350px'; + document.getElementById('div_oney_information_none').style.top = '100px'; + }, + // Banner information Oney None + _onMouseOutOneyInformation: function (ev) { + document.getElementById('div_oney_information_none').style.display = 'none'; + }, + // Banner Oney in cart payment + _onClickPaymentImage: function (ev) { + if (ev.handleObj.selector === 'a.js_payment_img-3x-gray') { + document.getElementById('checked_3_x').checked = true; + document.getElementById('checkbox_3x').style.opacity = '1.00'; + document.getElementById('payment_banner_img_3x').style.display = 'inline'; + document.getElementById('payment_banner_img_3x_gray').style.display = 'none'; + document.getElementById('table_3x_oney').style.display = 'inline'; + document.getElementById('checked_4_x').checked = false; + document.getElementById('checkbox_4x').style.opacity = '0.25'; + document.getElementById('payment_banner_img_4x').style.display = 'none'; + document.getElementById('payment_banner_img_4x_gray').style.display = 'inline'; + document.getElementById('table_4x_oney').style.display = 'none'; + } + if (ev.handleObj.selector === 'a.js_payment_img-4x-gray') { + document.getElementById('checked_3_x').checked = false; + document.getElementById('checkbox_3x').style.opacity = '0.25'; + document.getElementById('payment_banner_img_3x').style.display = 'none'; + document.getElementById('payment_banner_img_3x_gray').style.display = 'inline'; + document.getElementById('table_3x_oney').style.display = 'none'; + document.getElementById('checked_4_x').checked = true; + document.getElementById('checkbox_4x').style.opacity = '1.00'; + document.getElementById('payment_banner_img_4x').style.display = 'inline'; + document.getElementById('payment_banner_img_4x_gray').style.display = 'none'; + document.getElementById('table_4x_oney').style.display = 'inline'; + } + }, + // Banner information Oney None + _onMouseOverOneyInformationPayment: function (ev) { + document.getElementById('div_oney_information_payment_none').style.display = 'inline'; + document.getElementById('div_oney_information_payment_none').style.left = '-313px'; + document.getElementById('div_oney_information_payment_none').style.top = '-10px'; + }, + + _onMouseOutOneyInformationPayment: function (ev) { + document.getElementById('div_oney_information_payment_none').style.display = 'none'; + }, + + _onClickBannerOney: function (ev) { + // Call with Python Json RPC for get simulation Oney + ajax.jsonRpc('/shop/product/oney_simulation', 'call', { + 'product_total_price': document.getElementById('price_total_oney').value, + }).then(function (data) { + document.getElementById('amount_total_oney_3x').value = data['amount_total_oney']; + document.getElementById('contribution_of_3x').value = data['payment_3x']['contribution_of_3x']; + document.getElementById('1st_monthly_payment_3x').value = data['payment_3x']['1st_monthly_payment_3x']; + document.getElementById('2nd_monthly_payment_3x').value = data['payment_3x']['2nd_monthly_payment_3x']; + document.getElementById('total_payment_3x').value = data['payment_3x']['total_payment_3x']; + document.getElementById('amount_total_oney_4x').value = data['amount_total_oney']; + document.getElementById('contribution_of_4x').value = data['payment_4x']['contribution_of_4x']; + document.getElementById('1st_monthly_payment_4x').value = data['payment_4x']['1st_monthly_payment_4x']; + document.getElementById('2nd_monthly_payment_4x').value = data['payment_4x']['2nd_monthly_payment_4x']; + document.getElementById('3nd_monthly_payment_4x').value = data['payment_4x']['3nd_monthly_payment_4x']; + document.getElementById('total_payment_4x').value = data['payment_4x']['total_payment_4x']; + }); + document.getElementById('oney_popup').style.display = 'inline'; + }, + + _onClickBannerProvider: function (ev) { + // Call with Python Json RPC for get provider payplug + if ($(ev.currentTarget).prev('input').val(1).trigger('change')) { + var provider = ev.currentTarget.getAttribute("data-provider-payplug"); + var type_oney = 'oney_x3_with_fees'; + if (document.getElementById('checked_3_x')) { + if (document.getElementById('checked_4_x').checked == true) { + type_oney = 'oney_x4_with_fees' + } else { + type_oney = 'oney_x3_with_fees' + } + } + ajax.jsonRpc("/provider_payplug", 'call', {'provider_payplug': provider, 'type_oney': type_oney}); + } + }, +}); +}); + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/acquirer_payplug/views/payment_provider_oney_template.xml b/acquirer_payplug/views/payment_provider_oney_template.xml new file mode 100644 index 0000000..df78449 --- /dev/null +++ b/acquirer_payplug/views/payment_provider_oney_template.xml @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/acquirer_payplug/views/payment_provider_template.xml b/acquirer_payplug/views/payment_provider_template.xml new file mode 100644 index 0000000..b3b99c9 --- /dev/null +++ b/acquirer_payplug/views/payment_provider_template.xml @@ -0,0 +1,336 @@ + + + + + + + + + + + + + diff --git a/acquirer_payplug/views/payment_provider_view.xml b/acquirer_payplug/views/payment_provider_view.xml new file mode 100644 index 0000000..bdbf3ef --- /dev/null +++ b/acquirer_payplug/views/payment_provider_view.xml @@ -0,0 +1,168 @@ + + + + + + + payment.provider.form + payment.provider + + + + {'readonly': [('code','=','payplug')]} + + + + + + + + + {'readonly': [('code','=','payplug')]} + + + The configuration of the authorized countries is to be carried out for each method of payment! + + + {'invisible': [('code','=','payplug')]} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/acquirer_payplug/views/payment_transaction_view.xml b/acquirer_payplug/views/payment_transaction_view.xml new file mode 100644 index 0000000..915e62c --- /dev/null +++ b/acquirer_payplug/views/payment_transaction_view.xml @@ -0,0 +1,31 @@ + + + + + + + payment.transaction.tree + payment.transaction + + + + + + + + + + + payment.transaction.form + payment.transaction + + + + + + + + + + + \ No newline at end of file