Phase 1: Developer / Test / Simulation Mode
No real hardware required. Uses Stripe's test environment and simulated readers.
This module integrates Stripe Terminal for contactless in-person card payments directly inside Odoo 16.
It provides:
- Admin settings for Stripe API credentials
- PaymentIntent creation via Stripe API
- Simulated card-present payment collection
- Payment confirmation and status tracking
- Full audit log with chatter history in Odoo
- A setup wizard to create simulated readers without leaving Odoo
orbit_stripe_contactless_payment/
├── __init__.py
├── __manifest__.py
│
├── models/
│ ├── __init__.py
│ ├── stripe_terminal_payment.py # Core payment record model
│ ├── res_config_settings.py # Settings panel fields + wizard launcher
│ ├── stripe_setup_wizard.py # Transient wizard: test connection + create reader
│ └── ...
│
├── services/
│ ├── __init__.py
│ └── stripe_terminal_service.py # All Stripe API calls (Stripe v15 client API)
│
├── controllers/
│ ├── __init__.py
│ └── main.py # HTTP routes: connection token + webhook placeholder
│
├── views/
│ ├── stripe_terminal_payment_views.xml # Form, list, search views
│ ├── stripe_setup_wizard_views.xml # Setup & Test wizard UI
│ ├── res_config_settings_views.xml # Settings panel section
│ ├── menus.xml # Top-level menus
│ └── ...
│
├── security/
│ ├── groups.xml # Two groups: User and Manager
│ └── ir.model.access.csv # Model access rights
│
├── data/
│ └── ir_config_parameter_data.xml # Default system params + sequence
│
├── demo/
│ └── demo_data.xml # Sample records (succeeded, failed, draft)
│
└── static/
├── description/index.html
└── src/
├── js/stripe_terminal_widget.js # Phase 2 JS SDK stub
└── css/stripe_terminal.css # UI styles
Install the stripe Python package inside Docker:
docker exec -it odoo16_app pip3 install stripe
# Verify
docker exec -it odoo16_app python3 -c "import stripe; print(stripe._version.VERSION)"No changes needed to docker-compose.yml or odoo-server.conf.
The custom/addons path is already configured.
# Option A: via command line (recommended first time)
docker exec odoo16_app bash -c "
cd /app/odoo-server &&
python3 odoo-bin --config=odoo-server.conf \
-d odoo_16 -i orbit_stripe_contactless_payment --stop-after-init
"
# Option B: via Odoo UI
# Settings → Activate Developer Mode → Apps → Update Apps List
# Search "Orbit Stripe Terminal" → Installdocker exec -d odoo16_app bash -c "
cd /app/odoo-server &&
python3 odoo-bin --config=odoo-server.conf --dev=all
"docker exec odoo16_app bash -c "
pkill -f odoo-bin; sleep 2
cd /app/odoo-server &&
python3 odoo-bin --config=odoo-server.conf \
-d odoo_16 -u orbit_stripe_contactless_payment --stop-after-init
"| Group | Access |
|---|---|
| Stripe Terminal / User | Create & process payments |
| Stripe Terminal / Manager | Full access + Settings + Setup Wizard |
To add a user via UI:
Settings → Users & Companies → Groups → search "Stripe Terminal / Manager" → add user → Save
To add a user via shell (quick fix):
docker exec odoo16_app bash -c "
cd /app/odoo-server
python3 odoo-bin shell --config=odoo-server.conf -d odoo_16 --no-http 2>/dev/null <<'EOF'
grp = env.ref('orbit_stripe_contactless_payment.group_stripe_terminal_manager')
user = env['res.users'].search([('login', '=', 'your_login_here')], limit=1)
grp.sudo().write({'users': [(4, user.id)]})
env.cr.commit()
print('Done:', user.name, 'added to Stripe Terminal Manager')
EOF
"- Log in to https://dashboard.stripe.com
- Switch to Test Mode (toggle in top-left)
- Go to Developers → API Keys
- Copy:
- Secret key:
sk_test_... - Publishable key:
pk_test_...
- Secret key:
- In Stripe Dashboard → Terminal → Locations
- Click + New location → fill in any address → Save
- Copy the Location ID:
tml_...
- Open Odoo → Settings → General Settings
- Scroll down to "Stripe Terminal" section
- Fill in:
| Field | Value |
|---|---|
| Stripe Secret Key | sk_test_... |
| Stripe Publishable Key | pk_test_... |
| Terminal Location ID | tml_... |
| Reader ID | (leave blank — wizard will create it) |
| Test Mode | ON |
- Click Save
- In Settings → Stripe Terminal → click Open Setup & Test Wizard
- Click Test Stripe Connection → verify it shows SUCCESSFUL
- Click Create Simulated Reader → the Reader ID (
tmr_...) is auto-saved to Settings - Click Close
┌──────────────────────────────────────────────────────────────────────────────┐
│ STRIPE TERMINAL PAYMENT FLOW │
│ (Phase 1 — Simulated) │
└──────────────────────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────┬───────────────────┐
│ User Action │ Odoo Process │ Stripe API │
├───────────────┼──────────────────────────────────────────┼───────────────────┤
│ 1. New Payment│ User enters payment amount. │ │
│ │ Odoo creates a `stripe.terminal.payment` │ │
│ │ record. │ │
│ │ State: `Draft` │ │
├───────────────┼──────────────────────────────────────────┼───────────────────┤
│ 2. Create │ Odoo calls `stripe.PaymentIntent.create()` │
│ Payment Intent│ with `payment_method_types=['card_present']`. │
│ │ Stripe returns a PaymentIntent ID (`pi_...`) │
│ │ and `client_secret`. │
│ │ Odoo stores these values in the payment record. │
│ │ State: `Intent Created` │
├───────────────┼──────────────────────────────────────────┼───────────────────┤
│ 3. Simulate │ Odoo triggers `process_payment_intent()` │ Reader processes │
│ Card Tap │ followed by `present_payment_method()`. │ payment intent. │
│ │ A test helper simulates the customer │ Stripe simulates │
│ │ tapping their card. │ a card-present │
│ │ State: `Processing` │ transaction. │
├───────────────┼──────────────────────────────────────────┼───────────────────┤
│ 4. Confirm │ Odoo calls `PaymentIntent.retrieve()` │ Stripe returns │
│ Payment │ with `expand=['latest_charge']`. │ final payment │
│ │ Odoo stores the Charge ID (`ch_...`). │ status and charge │
│ │ State: `Succeeded` │ reference. │
└───────────────┴──────────────────────────────────────────┴───────────────────┘
Additional Notes:
- All steps are logged in the record’s Chatter (`message_ids`).
- All Stripe references such as `pi_...` and `ch_...` are stored in the Odoo record.
Draft
│
▼ [Create Payment Intent]
Intent Created
│
▼ [Simulate Card Tap]
Processing
│
▼ [Confirm Payment]
Succeeded or Failed
Any non-succeeded state can be → Cancelled
Failed / Cancelled can be → Reset to Draft (for retry)
- Go to Stripe Terminal (top menu) → New Payment
- Fill in:
- Description:
Test Payment - Table 5 - Amount:
25.00 - Currency:
USD
- Description:
- Click 1. Create Payment Intent
- Record shows:
stripe_payment_intent_id = pi_xxxxx - State → Intent Created
- Record shows:
- Click 2. Simulate Card Tap
- State → Processing
- Chatter: "Payment simulation initiated"
- Click 3. Confirm Payment
- State → Succeeded (green banner)
stripe_charge_id = ch_xxxxxvisible on record
- Go to https://dashboard.stripe.com/test/payments
- Your
pi_xxxxxpayment appears with status Succeeded - Click it to see full charge details, card details, and metadata
| URL | Method | Auth | Purpose |
|---|---|---|---|
/stripe/terminal/connection_token |
POST | user session | Returns Terminal JS SDK connection token |
/stripe/terminal/webhook |
POST | none | Stripe webhook receiver (Phase 2 placeholder) |
/stripe/terminal/readers |
POST | user session | Lists available readers |
| stripe version | Compatibility |
|---|---|
| v2 – v7 | Old-style API (stripe.PaymentIntent.create()) |
| v8 – v10 | Old-style still works with deprecation warnings |
| v11 – v15 | This module uses StripeClient (new API) |
This module uses stripe.StripeClient (introduced in v11) for all API calls — fully compatible with the installed v15.
| Limitation | Phase 2 Fix |
|---|---|
| No real hardware | Replace simulate_reader_payment() with real reader flow |
| Webhooks are log-only | Implement signature verification in controllers/main.py |
| No accounting entries | Add account.payment creation after payment succeeds |
| No POS integration | Link stripe.terminal.payment to pos.order |
| Manual 3-step flow | Automate via webhooks + reader event callbacks |
| JS SDK not loaded | Load https://js.stripe.com/terminal/v1/ + implement widget |
| No refund support | Add stripe.Refund.create() action |
When you're ready to connect a real Stripe Terminal reader (e.g. BBPOS WisePOS E, Stripe Reader S700):
- Replace
registration_code: 'simulated-wpe'with real device registration code - Implement webhook signature verification (
whsec_...) incontrollers/main.py - Load Stripe Terminal JS SDK in
stripe_terminal_widget.js - Implement
fetchConnectionToken→/stripe/terminal/connection_token - Implement
collectPaymentMethod+processPaymentin JS - Add
account.paymentjournal entry creation after payment succeeds - Add
pos.orderlinkage for POS use - Add refund flow via
stripe.Refund - Move
sk_live_...key into Odoo vault / environment variable (not Settings UI) - Set up Stripe webhook endpoint in Stripe Dashboard pointing to
/stripe/terminal/webhook
# Install stripe package
docker exec -it odoo16_app pip3 install stripe
# Install module (first time)
docker exec odoo16_app bash -c "cd /app/odoo-server && python3 odoo-bin --config=odoo-server.conf -d odoo_16 -i orbit_stripe_contactless_payment --stop-after-init"
# Upgrade module (after code changes)
docker exec odoo16_app bash -c "pkill -f odoo-bin; sleep 2; cd /app/odoo-server && python3 odoo-bin --config=odoo-server.conf -d odoo_16 -u orbit_stripe_contactless_payment --stop-after-init"
# Start Odoo
docker exec -d odoo16_app bash -c "cd /app/odoo-server && python3 odoo-bin --config=odoo-server.conf --dev=all"
# Tail Odoo log
docker exec odoo16_app tail -f /var/log/odoo.log
# Open Odoo shell
docker exec -it odoo16_app bash -c "cd /app/odoo-server && python3 odoo-bin shell --config=odoo-server.conf -d odoo_16 --no-http"- Stripe Terminal Docs
- Stripe Python SDK v15
- Stripe Terminal Test Cards
- Stripe Dashboard (Test Mode)
- Odoo 16 ORM Reference
Nazmus Shakib
Email: [email protected]
Maintenance & Support:
Feel free to open issues or submit pull requests. For custom solutions, collaborations, or business inquiries, please get in touch directly.