Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
2 changes: 1 addition & 1 deletion monero-rpc-odoo/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Categories can be used to filter modules in modules listing
# for the full list
"category": "Accounting",
"version": "14.0.1.0.2",
"version": "15.0",
# any module necessary for this one to work correctly
"depends": [
"website_sale",
Expand Down
176 changes: 34 additions & 142 deletions monero-rpc-odoo/controllers/monero_controller.py
Original file line number Diff line number Diff line change
@@ -1,152 +1,16 @@
import logging

from odoo import http
from odoo.addons.payment.controllers.portal import PaymentProcessing
# Cannot import, need to write it ourselfes...
# from odoo.addons.payment.controllers.portal import PaymentProcessing
from odoo.http import request
from monero.address import SubAddress

_logger = logging.getLogger(__name__)


class MoneroController(http.Controller):
@http.route("/shop/payment/monero/submit", type="json", auth="public", website=True)
def monero_transaction(self, verify_validity=False, **kwargs):
"""
Function creates a transaction and
payment token using the sessions sales order
Calls MoneroSalesOrder.salesorder_payment_sync()
:param verify_validity:
:param kwargs:
:return:
"""
sales_order = request.website.sale_get_order()
_logger.info(f"received sales_order: {sales_order.id}")
_logger.info(f"processing sales_order: {sales_order.id}")

# Ensure there is something to proceed
if not sales_order or (sales_order and not sales_order.order_line):
return False

assert sales_order.partner_id.id != request.website.partner_id.id
# at this time the sales order has to be in xmr
# the user cannot use a fiat pricelist when checking out
# this won't be fixed until after a job is added to automatically
# update res.currency.rate
currency = request.env["res.currency"].sudo().browse(sales_order.currency_id.id)
if currency.name != "XMR":
raise Exception(
"This pricelist is not supported, go back and select the "
"Monero Pricelist"
)

payment_acquirer_id = int(kwargs.get("acquirer_id"))

payment_partner_id = int(kwargs.get("partner_id"))

wallet_sub_address = SubAddress(kwargs.get("wallet_address"))

# security check, enforce one time usage of subaddresses
payment_tokens = (
request.env["payment.token"]
.sudo()
.search([("name", "=", wallet_sub_address)])
)
assert len(payment_tokens) < 1

# define payment token
payment_token = {
"name": wallet_sub_address.__repr__(),
"partner_id": payment_partner_id,
# partner_id creating sales order
"active": False,
# token shoudn't be active, the subaddress shouldn't be reused
"acquirer_id": payment_acquirer_id,
# surrogate key for payment acquirer
"acquirer_ref": "payment.payment_acquirer_monero_rpc",
}

_logger.info(f"creating payment token " f"for sales_order: {sales_order.id}")
token = request.env["payment.token"].sudo().create(payment_token)
token_id = token.id
token_short_name = token.short_name

# assign values for transaction creation
tx_val = {
"amount": sales_order.amount_total,
"reference": sales_order.name,
"currency_id": sales_order.currency_id.id,
"partner_id": sales_order.partner_id.id,
# Referencing the Sale Order Partner ID
"payment_token_id": token_id, # Associating the Payment Token ID.
"acquirer_id": payment_acquirer_id, # Payment Acquirer - Monero
"state": "pending",
# tx is pending,
# because the customer will know the address to send the tx to,
# but hasn't yet sent it
}

_logger.info(f"getting the transaction " f"for sales_order: {sales_order.id}")
# transaction = sales_order._create_payment_transaction(tx_val)
transaction = sales_order.get_portal_last_transaction()
if transaction.id is False:
transaction = sales_order._create_payment_transaction(tx_val)
_logger.info(f"created transaction: {transaction.id}")
else:
_logger.info(f"retrieved transaction: {transaction.id}")

# store the new transaction into
# the transaction list and if there's an old one, we remove it
# until the day the ecommerce supports multiple orders at the same time
last_tx_id = request.session.get("__website_sale_last_tx_id")
last_tx = request.env["payment.transaction"].browse(last_tx_id).sudo().exists()
if last_tx:
PaymentProcessing.remove_payment_transaction(last_tx)
PaymentProcessing.add_payment_transaction(transaction)
request.session["__website_sale_last_tx_id"] = transaction.id

# Sale Order is quotation sent
# , so the state should be set to "sent"
# , until the transaction has been verified
_logger.info(
f'setting sales_order state to "sent" ' f"for sales_order: {sales_order.id}"
)
request.env.user.sale_order_ids.sudo().update(
{"require_payment": "true", "state": "sent"}
)

payment_acquirer = (
request.env["payment.acquirer"].sudo().browse(payment_acquirer_id)
)
# set queue channel and max_retries settings
# for queue depending on num conf settings
num_conf_req = int(payment_acquirer.num_confirmation_required)
if num_conf_req == 0:
queue_channel = "monero_zeroconf_processing"
queue_max_retries = 44
else:
queue_channel = "monero_secure_processing"
queue_max_retries = num_conf_req * 25

# Add payment token and sale order to transaction processing queue

sales_order.with_delay(
channel=queue_channel, max_retries=queue_max_retries
).process_transaction(transaction, token, num_conf_req)

if transaction:
res = {
"result": True,
"id": token_id,
"short_name": token_short_name,
"3d_secure": False,
"verified": False,
}

if verify_validity is not False:
token.validate()
res["verified"] = token.verified

return res
_accept_url = '/payment/monero/feedback'

@http.route(
"/shop/payment/token", type="http", auth="public", website=True, sitemap=False
Expand Down Expand Up @@ -194,7 +58,7 @@ def payment_token(self, pm_id=None, **kwargs):
# transaction was already
# established in /shop/payment/monero/submit
transaction = request.env["payment.transaction"].sudo().browse(tx_id)
PaymentProcessing.add_payment_transaction(transaction)
add_payment_transaction(transaction)
# clear the tx in session, because we're done with it
request.session["__website_sale_last_tx_id"] = None
return request.redirect("/shop/payment/validate")
Expand All @@ -221,8 +85,36 @@ def payment_token(self, pm_id=None, **kwargs):
f'"pending" for sales_order: {sales_order.id}'
)
transaction.sudo().update({"state": "pending"})
PaymentProcessing.add_payment_transaction(transaction)
add_payment_transaction(transaction)
return request.redirect("/shop/payment/validate")

PaymentProcessing.add_payment_transaction(transaction)
add_payment_transaction(transaction)
return request.redirect("/payment/process")

@http.route(_accept_url, type='http', auth='public', methods=['POST'], csrf=False)
def transfer_form_feedback(self, sale_id=None, **post):
# calls monero_transaction _get_tx_from_feedback_data
if sale_id:
request.session["sale_last_order_id"] = sale_id
request.env['payment.transaction'].sudo()._handle_feedback_data('monero-rpc', post)
return request.redirect("/payment/status")


def remove_payment_transaction(transactions):
tx_ids_list = request.session.get("__payment_tx_ids__", [])
if transactions:
for tx in transactions:
if tx.id in tx_ids_list:
tx_ids_list.remove(tx.id)
else:
return False
request.session["__payment_tx_ids__"] = tx_ids_list
return True


def add_payment_transaction(transactions):
if not transactions:
return False
tx_ids_list = set(request.session.get("__payment_tx_ids__", [])) | set(transactions.ids)
request.session["__payment_tx_ids__"] = list(tx_ids_list)
return True
3 changes: 2 additions & 1 deletion monero-rpc-odoo/controllers/website_sale.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def payment(self, **post):
did go to a payment.acquirer website but closed the tab without
paying / canceling
"""
_logger.info("In Payment")
order = request.website.sale_get_order()
redirection = self.checkout_redirection(order)
if redirection:
Expand Down Expand Up @@ -77,7 +78,7 @@ def payment(self, **post):
)

request.wallet_address = wallet.new_address()[0]
_logger.debug("new monero payment subaddress generated")
_logger.info("new monero payment subaddress generated")

if render_values["errors"]:
render_values.pop("acquirers", "")
Expand Down
23 changes: 18 additions & 5 deletions monero-rpc-odoo/data/monero_xmr_payment_acquirer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@
<data noupdate="1">
<template id="xmr_payment_form">
<input type="hidden" name="data_set" data-create-route="/shop/payment/monero/submit"/>
<input type="hidden" id="wallet_address" name="wallet_address" data-is-required="true" t-att-value="request.wallet_address"/>
<input type="hidden" id="wallet_address" name="wallet_address" data-is-required="true" t-att-value="wallet_address"/>
<input type="hidden" name="type" t-att-value="'XMR'"/>
<input type="hidden" name="acquirer_id" t-att-value="id"/>
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input t-if="return_url" type="hidden" name="return_url" t-att-value="return_url"/>
<input t-if="partner_id" type="hidden" name="partner_id" t-att-value="partner_id"/>
</template>

<template id="xmr_acquirer_button"></template>

<template id="redirect_form">
<form t-att-action="api_url" method="post">
<input type="hidden" name="reference" t-att-value="reference"/>
</form>
</template>

<record id="payment_acquirer_monero_rpc" model="payment.acquirer">
<field name="name">Monero RPC</field>
<field name="provider">monero-rpc</field>
Expand All @@ -23,10 +28,12 @@
<!-- Odoo bug, the payment icon isn't generated yet so there will be an-->
<!-- error on install, the user has to manually set the payment icon-->
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="xmr_acquirer_button"/>
<field name="registration_view_template_id" ref="xmr_payment_form"/>
<!-- <field name="view_template_id" ref="xmr_acquirer_button"/> -->
<!-- The whole wallet subaddress was actually done before the user checked Monero...will not do that anymore...
<field name="inline_form_view_id" ref="xmr_payment_form"/> -->
<field name="redirect_form_view_id" ref="redirect_form"/>
<field name="state">test</field>
<field name="payment_flow">s2s</field>
<!-- <field name="payment_flow">s2s</field> -->
<field name="pre_msg">
<![CDATA[<p>You will be redirected to the Payment information and processing page after clicking on the payment button.</p>]]>
</field>
Expand All @@ -48,5 +55,11 @@
<field name="monero_rpc_config_password">password</field>
<field name="display_as">Monero</field>
</record>

<record id="payment_method_monero" model="account.payment.method">
<field name="name">Monero-RPC</field>
<field name="code">monero-rpc</field>
<field name="payment_type">inbound</field>
</record>
</data>
</odoo>
2 changes: 1 addition & 1 deletion monero-rpc-odoo/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import monero_acq, sales_order, exceptions
from . import monero_acq, sales_order, exceptions, monero_payment_transaction, account_payment_method

# TODO automate prices list for currencies,
# the lists would be updated at a chosen interval with the correct conversion
Expand Down
13 changes: 13 additions & 0 deletions monero-rpc-odoo/models/account_payment_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-

from odoo import api, models


class AccountPaymentMethod(models.Model):
_inherit = 'account.payment.method'

@api.model
def _get_payment_method_information(self):
res = super()._get_payment_method_information()
res['monero-rpc'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
return res
8 changes: 8 additions & 0 deletions monero-rpc-odoo/models/monero_acq.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,11 @@ def check_rpc_server_connection(self):
help="Required Number of confirmations "
"before an order's transactions is set to done",
)

def _get_default_payment_method_id(self):
self.ensure_one()
if self.provider != 'monero-rpc':
return super()._get_default_payment_method_id()
_logger.warning(self.env)
_logger.warning(dir(self.env))
return self.env.ref('monero-rpc-odoo.payment_method_monero').id
Loading