Enable Dark Mode!
odoo-14-pos-payment-terminal-integration.jpg
By: Noushid Khan

Odoo 14 POS Payment Terminal Integration

Functional Odoo 14 POS

The POS (Point of Sale) payment terminal is an electronic device used for card payments via POS.
odoo-14-pos-payment-terminal-integration-cybrosys
You can sync or connect it with our Odoo. It usually works in the following steps.
    1. Read request.
    2. Reads information from the card and checks for AN adequate account balance.
    3. Transfer money from customer account to seller account.
    4. Sent response.
    5. Records the transaction. 
    6. Prints the receipts.
We already know that Odoo has some example payment terminals, so don’t worry about the complications of integrating another device in Odoo.
odoo-14-pos-payment-terminal-integration-cybrosys
These are the POS payment terminals that are already integrated into our Odoo system, so if you are a beginner integrating a payment terminal, you can refer to their codes before starting, which is just an option.
Through this blog, I will share the basic idea of ??how to integrate a new POS Payment Terminal in Odoo.
We have a source code to manage the POS Payment Terminal in the point_of_sale module, which is named as payment.js 
odoo-14-pos-payment-terminal-integration-cybrosys
payment.js
odoo.define('point_of_sale.PaymentInterface', function (require) {
"use strict";
 
var core = require('web.core');
 
/**
* Implement this interface to support a new payment method in the POS:
*
* var PaymentInterface = require('point_of_sale.PaymentInterface');
* var MyPayment = PaymentInterface.extend({
* ...
* })
*
* To connect the interface to the right payment methods register it:
*
* var models = require('point_of_sale.models');
* models.register_payment_method('my_payment', MyPayment);
*
* my_payment is the technical name of the added selection in
* use_payment_terminal.
*
* If necessary new fields can be loaded on any model:
*
* models.load_fields('pos.payment.method', ['new_field1', 'new_field2']);
*/
var PaymentInterface = core.Class.extend({
init: function (pos, payment_method) {
this.pos = pos;
this.payment_method = payment_method;
this.supports_reversals = false;
},
 
/**
* Call this function to enable UI elements that allow a user to
* reverse a payment. This requires that you implement
* send_payment_reversal.
*/
enable_reversals: function () {
this.supports_reversals = true;
},
 
/**
* Called when a user clicks the "Send" button in the
* interface. This should initiate a payment request and return a
* Promise that resolves when the final status of the payment line
* is set with set_payment_status.
*
* For successful transactions set_receipt_info() should be used
* to set info that should to be printed on the receipt. You
* should also set card_type and transaction_id on the line for
* successful transactions.
*
* @param {string} cid - The id of the paymentline
* @returns {Promise} resolved with a boolean that is false when
* the payment should be retried. Rejected when the status of the
* paymentline will be manually updated.
*/
send_payment_request: function (cid) {},
 
/**
* Called when a user removes a payment line that's still waiting
* on send_payment_request to complete. Should execute some
* request to ensure the current payment request is
* cancelled. This is not to refund payments, only to cancel
* them. The payment line being cancelled will be deleted
* automatically after the returned promise resolves.
*
* @param {} order - The order of the paymentline
* @param {string} cid - The id of the paymentline
* @returns {Promise}
*/
send_payment_cancel: function (order, cid) {},
 
/**
* This is an optional method. When implementing this make sure to
* call enable_reversals() in the constructor of your
* interface. This should reverse a previous payment with status
* 'done'. The paymentline will be removed based on returned
* Promise.
*
* @param {string} cid - The id of the paymentline
* @returns {Promise} returns true if the reversal was successful.
*/
send_payment_reversal: function (cid) {},
 
/**
* Called when the payment screen in the POS is closed (by
* e.g. clicking the "Back" button). Could be used to cancel in
* progress payments.
*/
close: function () {},
});
 
return PaymentInterface;
});
You can read the code above carefully and use those functions in our custom module. Let me explain how to use it.
Create a payment terminal module folder, the technical name like ‘pos_’ followed by your terminal name. Eg: pos_adyen, pos_six, etc.
First, we can set the configuration for our payment terminal in res.config.settings
odoo-14-pos-payment-terminal-integration-cybrosys
res_config_settings.py
from odoo import fields, models
 
class ResConfigSettings(models.TransientModel):
      _inherit = 'res.config.settings'
 
      module_pos_terminal_name = fields.Boolean(string="Payment Terminal",
                                            help="The transactions are processed by payment terminal."
                                                             " Set your terminal credentials on the related payment method.")
 
      def set_values(self):
             super(ResConfigSettings, self).set_values()
             payment_methods = self.env['pos.payment.method']
             if not self.env['ir.config_parameter'].sudo().get_param('pos_terminal_name.module_pos_terminal_name'):
                    payment_methods |= payment_methods.search([('use_payment_terminal', '=', 'terminal_name')])
                    payment_methods.write({'use_payment_terminal': False})
 NB: The field name should start with "module_" and the module name. So when we disable the option it will uninstall the module automatically.
res_config_setting_views.xml 
<?xml version="1.0" encoding="utf-8"?>
<odoo>
   <record id="res_config_view_form_inherit_pos_econduit" model="ir.ui.view">
         <field name="name">res.config.form.inherit.pos.terminal</field>
         <field name="model">res.config.settings</field>
 
         <field name="inherit_id" ref="point_of_sale.res_config_settings_view_form"/>
         <field name="arch" type="xml">
               <div id="o_settings_use_payment_terminals" position="inside">
                      <div class="col-12 col-lg-6 o_setting_box" title="The transactions are processed by terminal. Set your
                                                                                                    terminal device on the related payment method.">
                            <div class="o_setting_left_pane">
                                     <field name="module_pos_terminal_name"/>
                            </div>
                            <div class="o_setting_right_pane">
                                     <label for="module_pos_terminal_name" string="POS Payment Terminal"/>
                                     <div class="text-muted">
                                            Accept payments with an payment payment terminal
                                     </div>
                            </div>
                      </div>
             </div>
         </field>
  </record>
</odoo>
Then inherit our pos.payment.method model adds your terminal name to the existing payment terminal selection field and also create your own fields for entering credentials related to your payment terminal like as follows.
pos_payment_method.py
from odoo import fields, models
 
class PosPaymentMethod(models.Model):
      _inherit = 'pos.payment.method'
 
      def _get_payment_terminal_selection(self):
              return super(PosPaymentMethod, self)._get_payment_terminal_selection() + [('terminal_name', 'Terminal Name')]
    
   #credentials
      terminal_api_key = fields.Char('API Key')
      terminal_api_pwd = fields.Char('API Password')
      terminal_id = fields.Char('Terminal ID')
 “_get_payment_terminal_selection” is the default function for adding our terminal name to an existing payment terminal selection field. Select our terminal from were and create a payment method for our terminal.
pos_payment_method_views.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="pos_payment_method_view_form_inherit_pos_econduit" model="ir.ui.view">
        <field name="name">pos.payment.method.form.inherit.terminal</field>
        <field name="model">pos.payment.method</field>
        <field name="inherit_id" ref="point_of_sale.pos_payment_method_view_form"/>
        <field name="arch" type="xml">
           <xpath expr="//field[@name='use_payment_terminal']" position="after">
              <field name="terminal_api_key"
                        attrs="{'invisible': [('use_payment_terminal', '!=', 'terminal_name')],
                                     'required': [('use_payment_terminal', '=', 'terminal_name')]}"/>
              <field name="terminal_api_pwd" password="True"
                        attrs="{'invisible': [('use_payment_terminal', '!=', 'terminal_name')],
                                     'required': [('use_payment_terminal', '=', 'terminal_name')]}"/>
              <field name="terminal_id"
                        attrs="{'invisible': [('use_payment_terminal', '!=', 'terminal_name')],
                                     'required': [('use_payment_terminal', '=', 'terminal_name')]}"/>
           </xpath>
        </field>
    </record>
</odoo>
The view becomes,
odoo-14-pos-payment-terminal-integration-cybrosys
Then load your payment method model and its fields.
odoo.define('pos_terminal_name.models', function(require) {
        var models = require('point_of_sale.models');
        var PaymentTerminal = require('pos_terminal_name.payment');
 
        models.register_payment_method('terminal_name', PaymentTerminal);
        models.load_fields('pos.payment.method', ['terminal_api_key', 'terminal_api_pwd', 'terminal_id']);
 
       var _payterminal = models.Paymentline.prototype;
       models.Paymentline = models.Paymentline.extend({
            init_from_JSON: function(json) {
               _payterminal.init_from_JSON.apply(this, arguments);
                   //pass your credentials like
                  //this.last_digits = json.last_digits;
                },
            export_as_JSON: function() {
                 return _.extend(_payeconduit.export_as_JSON.apply(this, arguments), {
                  //pass your credentials like
                  //this.last_digits = json.last_digits;
            });
            },
      });
});
Then extend your payment interface.
odoo.define('pos_terminal_name.payment', function(require) {
"use strict";
     var core = require('web.core');
     var PaymentInterface = require('point_of_sale.PaymentInterface');
     const { Gui } = require('point_of_sale.Gui');
 
     var _t = core._t;
 
     var PaymentTerminal = PaymentInterface.extend({
 
            send_payment_request: function(cid) {
                this._super.apply(this, arguments);
                var line = this.pos.get_order().selected_paymentline;
                var order = this.pos.get_order();
                var data = this._terminal_pay_data();
                var apikey = data.PaymentMethod.terminal_api_key
                var apipswd = data.PaymentMethod.terminal_api_pwd
                var terminalId = data.PaymentMethod.terminal_id
               // You can send your request from here to the terminal, and based on the response from your
              // terminal you can set payment_status to success / retry / waiting.
                line.set_payment_status('success');
                return new Promise(function(resolve) {
                    self._waitingResponse = self._waitingCancel;
                 });
             },
 
           _terminal_pay_data: function() {
                  var order = this.pos.get_order();
                  var line = order.selected_paymentline;
                  var data = {
                        'Name': order.name,
                        'OrderID': order.uid,
                        'TimeStamp': moment().format(),
                        'Currency': this.pos.currency.name,
                        'RequestedAmount': line.amount,
                        'PaymentMethod': this.payment_method
                  };
                 return data;
             },
     });
    return PaymentTerminal;
});
Here you can see a “send_payment_request” function which is already defined within our source code (payment.js) of the point_of_sale module. Within this function you can write your code for sending a request to the POS terminal, then you can set the payment_status done /retry/waiting based on the response gets from your terminal.
odoo-14-pos-payment-terminal-integration-cybrosys
If the response from the terminal unsuccessful after swiping your card, the payment interface is shown as shown below.
odoo-14-pos-payment-terminal-integration-cybrosys
If successful, you can validate the payment.
Note that this is how a POS payment terminal actually works in Odoo, and this is a basic example of how to integrate a POS payment terminal in Odoo POS. Therefore, the complexity may vary depending on the nature of the terminal machine.
We hope this blog helps you integrate the POS payment terminal into Odoo14.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message