# 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