Overview
This case study covers a custom Odoo 17 Point of Sale module,, that takes card payments on a Moneris terminal directly from the POS. The cashier sends the amount from the payment screen, the terminal prompts the customer, and Odoo follows the result until the payment is approved or declined. The module combines backend Python (terminal settings, sync and purchase requests, receipt checks) with frontend JavaScript (payment line lifecycle and status polling).
Business Challenge
Odoo 17 has no Moneris terminal integration, so card payments on a Moneris terminal were handled outside the POS order. That left room for mismatched amounts and no link between what the terminal did and the POS payment line. The team needed the POS to send the exact amount to the terminal, keep the terminal ready, follow the result without the cashier re-checking it, and keep the API credentials out of the browser.
Solution & Approach
The feature was built as a standard Odoo 17 payment terminal integration, in a few parts:
- Added a Moneris option to the POS payment method, with terminal settings for Terminal ID, IST config code, API token, Store ID and Merchant ID, plus a Moneris switch in the POS settings.
- Wrote backend methods that call the Moneris cloud terminal API: a terminal sync, and a purchase request carrying the order name, an idempotency key and the amount in cents. A system parameter selects the test or production endpoint.
- Built the POS payment interface so the payment line moves through waiting, done and force-done states in step with the terminal.
- Polled the Moneris receipt URL through Odoo every 2 seconds, up to 60 times, so the browser never holds the API credentials.
- Re-synced the terminal automatically when the last sync was more than an hour old.
- Patched the payment screen to reset stale Moneris lines after a page reload.
- Added a bus message and notification route meant to push results to the POS. This part is still a prototype.
- Reviewed the code and recorded the defects and a production checklist in the technical documentation.
Key Technical Highlights
- Moneris logic lives in model methods on
pos.payment.method, which return one {error, message} shape on failure.
- Credentials stay on the server: the POS does not load the Moneris fields into the browser, and all Moneris calls go through Odoo.
- Purchase requests carry an idempotency key built from the date and the order name, and amounts are converted to cents with decimal rounding.
- Terminal sync polls until Moneris reports completion and refreshes automatically after an hour.
- Status handling maps Moneris codes to approved, declined or pending, and keeps the payment line in step.
- Recovery after a reload: a stale Moneris line is reset to retry when the payment screen opens.
- Two status paths: receipt polling, and a bus notification for faster updates once the route is finished.
Outcome
Cashiers can take a Moneris card payment from the Odoo 17 POS screen: the amount goes to the terminal and the approval or decline comes back to the payment line, with card details stored on the line. The code review also lists what remains before production use: finish the notification route, restrict the API token, use retry instead of force done for declines, and store card details per attempt.
Technology Stack
Odoo 17 (Point of Sale), Python with requests, JavaScript (OWL), Odoo bus, Moneris cloud terminal API (JSON over HTTPS).