[IMP][ADD] payment_hyperpay_tokenization
This commit is contained in:
parent
d0199aa5e9
commit
6a021918a8
|
|
@ -22,6 +22,7 @@ class DonationRecurring(models.Model):
|
|||
string="Next Donation Date", tracking=True,
|
||||
help="The date when the next donation will be processed.", default=lambda self: fields.Date.context_today(self)
|
||||
)
|
||||
send_recurring_sms = fields.Boolean()
|
||||
recurring_interval = fields.Integer(
|
||||
string="Repeat Every",
|
||||
default=1,
|
||||
|
|
@ -39,10 +40,11 @@ class DonationRecurring(models.Model):
|
|||
)
|
||||
active = fields.Boolean(default=True)
|
||||
state = fields.Selection([
|
||||
('new', 'New'),
|
||||
('active', 'Active'),
|
||||
('paused', 'Paused'),
|
||||
('cancel', 'Cancelled'),
|
||||
], default='active', string='Status', tracking=True)
|
||||
], default='new', string='Status', tracking=True)
|
||||
recurring_line_ids = fields.One2many(
|
||||
'donation.recurring.line',
|
||||
'recurring_id',
|
||||
|
|
@ -150,6 +152,7 @@ class DonationRecurring(models.Model):
|
|||
sale_final_vals = sale_draft._convert_to_write(sale_draft._cache)
|
||||
sale_order = self.env['sale.order'].sudo().create(sale_final_vals)
|
||||
|
||||
line.sale_order_id = sale_order.id
|
||||
# 4. Prepare Sale Order Line vals
|
||||
line_vals = {
|
||||
'order_id': sale_order.id,
|
||||
|
|
@ -168,17 +171,18 @@ class DonationRecurring(models.Model):
|
|||
line_final_vals = line_draft._convert_to_write(line_draft._cache)
|
||||
self.env['sale.order.line'].sudo().create(line_final_vals)
|
||||
|
||||
# 7. Confirm the order
|
||||
sale_order.action_confirm()
|
||||
|
||||
# 8. Link the recurring line
|
||||
line.sale_order_id = sale_order.id
|
||||
|
||||
confirmed = self._recurring_confirm_sale_order(sale_order)
|
||||
if confirmed:
|
||||
return sale_order
|
||||
return False
|
||||
|
||||
def _recurring_confirm_sale_order(self, order):
|
||||
order.with_context(skip_donation_sms=not self.send_recurring_sms).action_confirm()
|
||||
self.message_post(
|
||||
body=_("✅ Sale Order <b>%s</b> created for donation dated %s.") % (sale_order.name, line.date)
|
||||
body=_("✅ Sale Order <b>%s</b> created for donation dated %s.") % (order.name, order.date_order)
|
||||
)
|
||||
return True
|
||||
|
||||
return sale_order
|
||||
|
||||
# def process_delivery_from_order(self, order, quantity_done=None):
|
||||
# picking = order.picking_ids.filtered(lambda p: p.state not in ('done', 'cancel'))
|
||||
|
|
@ -201,6 +205,26 @@ class DonationRecurring(models.Model):
|
|||
invoice.action_post()
|
||||
self.message_post(body=_("🧾 Invoice <b>%s</b> posted for Sale Order <b>%s</b>.") % (invoice.name, order.name))
|
||||
return invoice
|
||||
|
||||
def action_activate(self):
|
||||
for rec in self:
|
||||
rec.name = self.env['ir.sequence'].next_by_code('donation.recurring') or _('New')
|
||||
for line in rec.recurring_line_ids:
|
||||
order = rec._create_sale_order(line)
|
||||
if not order:
|
||||
continue
|
||||
rec.sudo().create_invoice_from_order(order)
|
||||
rec._advance_next_date()
|
||||
rec.state = 'active'
|
||||
template = self.env.company.donation_recurring_created_sms_template_id
|
||||
if not template:
|
||||
raise ValidationError(_("⚠️ SMS template for 'Send When Created' is not configured in Company settings."))
|
||||
|
||||
rec._message_sms_with_template(
|
||||
template=template,
|
||||
partner_ids=rec.partner_id.ids,
|
||||
put_in_queue=True
|
||||
)
|
||||
|
||||
def action_pause(self):
|
||||
for record in self:
|
||||
|
|
@ -307,28 +331,6 @@ class DonationRecurring(models.Model):
|
|||
elif rec.frequency == 'monthly':
|
||||
rec.recurring_next_date += relativedelta(months=interval)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if vals.get('name', _('New')) == _('New'):
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('donation.recurring') or _('New')
|
||||
res = super(DonationRecurring, self).create(vals)
|
||||
for line in res.recurring_line_ids:
|
||||
order = res._create_sale_order(line)
|
||||
if not order:
|
||||
continue
|
||||
res.sudo().create_invoice_from_order(order)
|
||||
res._advance_next_date()
|
||||
template = self.env.company.donation_recurring_created_sms_template_id
|
||||
if not template:
|
||||
raise ValidationError(_("⚠️ SMS template for 'Send When Created' is not configured in Company settings."))
|
||||
|
||||
res._message_sms_with_template(
|
||||
template=template,
|
||||
partner_ids=res.partner_id.ids,
|
||||
put_in_queue=True
|
||||
)
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
for rec in self:
|
||||
if rec.state == 'active':
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class SaleOrder(models.Model):
|
|||
sms_template_id = self.env.ref('ensan_sale_management.sms_template_data_donation')
|
||||
donar_sms_template_id = self.env.ref('ensan_sale_management.sms_template_donors_data_donation')
|
||||
for rec in self:
|
||||
if rec.state == 'sale':
|
||||
if rec.state == 'sale' and not self._context.get('skip_donation_sms'):
|
||||
if rec.order_mobile_number:
|
||||
rec._message_sms_with_template(
|
||||
template=sms_template_id,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
from . import controllers
|
||||
from . import models
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
'name': 'payment_hyperpay_tokenization',
|
||||
'version': '1.0',
|
||||
'description': '',
|
||||
'summary': '',
|
||||
'author': '',
|
||||
'website': '',
|
||||
'license': 'LGPL-3',
|
||||
'category': '',
|
||||
'depends': [
|
||||
'base', 'payment_hyperpay'
|
||||
],
|
||||
'data': [
|
||||
'views/payment_acquirer_views.xml'
|
||||
]
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import main
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
import requests
|
||||
import re
|
||||
|
||||
from odoo.http import route, request, Controller
|
||||
from odoo.addons.payment_hyperpay.data.payment_icon import payment_icon
|
||||
from odoo.addons.payment.controllers.portal import PaymentProcessing
|
||||
|
||||
|
||||
|
||||
TEST_URL = "https://eu-test.oppwa.com"
|
||||
LIVE_URL = "https://eu-prod.oppwa.com"
|
||||
|
||||
class HyperPayTokenization(Controller):
|
||||
|
||||
@route('/hyperpay/tokens/checkout', type='json', auth='public', website=True, methods=['POST'])
|
||||
def token_checkout(self, **kwargs):
|
||||
if not kwargs.get('acquirer_id'):
|
||||
return {'state': False, 'message': 'Couldn\'t identify acquirer'}
|
||||
try:
|
||||
self._save_session_data(kwargs)
|
||||
checkout_data = self._register_payment_card(kwargs)
|
||||
return checkout_data
|
||||
except Exception as er:
|
||||
return {
|
||||
'state': False,
|
||||
'message': er
|
||||
}
|
||||
|
||||
def _save_session_data(self, data):
|
||||
# save data in session as a form of naive saving
|
||||
return True
|
||||
|
||||
def _register_payment_card(self, data):
|
||||
acquirer_id = int(data.get('acquirer_id'))
|
||||
acquirer = request.env['payment.acquirer'].sudo().search([('id', '=', acquirer_id)])
|
||||
payment_icons = acquirer.payment_icon_ids.mapped('name')
|
||||
data_brands = "VISA"
|
||||
if (len(payment_icon) > 1):
|
||||
brands = [payment_icon[i.upper()] for i in payment_icons if i.upper() in payment_icon.keys()]
|
||||
data_brands = brands and " ".join(brands) or data_brands
|
||||
base_url = request.httprequest.host_url
|
||||
payload = {
|
||||
"entityId": acquirer.hyperpay_merchant_id,
|
||||
'createRegistration': True,
|
||||
}
|
||||
|
||||
payload.update(self._get_hyperpay_token_custom_parameters(data))
|
||||
|
||||
if acquirer.state == 'test':
|
||||
domain = TEST_URL
|
||||
else:
|
||||
domain = LIVE_URL
|
||||
|
||||
url = f"{domain}/v1/checkouts"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {acquirer.hyperpay_authorization}"
|
||||
}
|
||||
|
||||
response = requests.post(url=url, data=payload, headers=headers).json()
|
||||
|
||||
result = response.get('result', {})
|
||||
result_code = result.get('code')
|
||||
|
||||
if result_code and not re.match(r"^(000\.000\.|000\.100\.1|000\.[36]|000\.400\.1[12]0|000\.400\.0[^3]|000\.400\.100|000\.200)", result_code):
|
||||
return {'state': False, 'message': result.get('description', '')}
|
||||
|
||||
return_url = f'{base_url}hyperpay/tokens/result?acquirer_id={acquirer_id}'
|
||||
|
||||
return {
|
||||
'state': True,
|
||||
'checkout_id': response.get('id'),
|
||||
'service_domain': domain,
|
||||
'base_url': base_url,
|
||||
'data_brands': data_brands,
|
||||
'return_url': return_url,
|
||||
}
|
||||
|
||||
def _get_hyperpay_token_custom_parameters(self, data):
|
||||
reference_id = request.session.get('hyperpay_token_reference_id')
|
||||
reference_model = request.session.get('hyperpay_token_reference_model')
|
||||
return {
|
||||
"customParameters[SHOPPER_acquirer_id]": data.get('acquirer_id', 0),
|
||||
"customParameters[SHOPPER_hyperpay_token_reference_id]": reference_id,
|
||||
"customParameters[SHOPPER_hyperpay_token_reference_model]": reference_model,
|
||||
}
|
||||
|
||||
@route('/hyperpay/tokens/result', type='http', auth='public', website=True)
|
||||
def token_return(self, **post):
|
||||
try:
|
||||
if post.get('transaction_id'):
|
||||
transaction = request.env['payment.transaction'].sudo().search([('id', '=', int(post.get('transaction_id')))])
|
||||
if transaction:
|
||||
transaction.s2s_do_refund()
|
||||
else:
|
||||
acquirer_id = request.env['payment.acquirer'].sudo().search([('id', '=', int(post.get('acquirer_id', 0)))])
|
||||
|
||||
if acquirer_id.state == 'test':
|
||||
domain = TEST_URL
|
||||
else:
|
||||
domain = LIVE_URL
|
||||
|
||||
url = f"{domain}{post.get('resourcePath')}?entityId={acquirer_id.hyperpay_merchant_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {acquirer_id.hyperpay_authorization}"
|
||||
}
|
||||
resp = requests.get(url=url, headers=headers).json()
|
||||
result = resp.get('result', {})
|
||||
result_code = result.get('code')
|
||||
|
||||
if result_code and not re.match(r"^(000\.000\.|000\.100\.1|000\.[36]|000\.400\.1[12]0|000\.400\.0[^3]|000\.400\.100|000\.200)", result_code):
|
||||
# Handle failed cards logic here
|
||||
return {'state': False, 'message': result.get('description', ''), 'errors': resp.get('parameterErrors', [])}
|
||||
|
||||
# create card record and activate recurring donation
|
||||
card = resp.get('card', {})
|
||||
if not card:
|
||||
return {'state': False, 'message': 'Card data not found'}
|
||||
self._post_process_token_return(resp)
|
||||
except Exception as er:
|
||||
request.env.cr.rollback()
|
||||
raise er
|
||||
|
||||
return request.redirect('/my/recurring_donation')
|
||||
|
||||
def _post_process_token_return(self, data):
|
||||
acquirer_id = int(data.get('customParameters', {}).get('SHOPPER_acquirer_id', 0))
|
||||
card = data.get('card', {})
|
||||
card_vals = {
|
||||
'name': f"{card.get('bin', '')}XXXXXXXXXXXX{card.get('last4Digits', '')}",
|
||||
'partner_id': request.env.user.partner_id.id,
|
||||
'acquirer_id': acquirer_id,
|
||||
'acquirer_ref': data.get('id', ''),
|
||||
'hyperpay_payment_brand': data.get('paymentBrand'),
|
||||
}
|
||||
|
||||
token_id = request.env['payment.token'].create(card_vals)
|
||||
tx = token_id.validate()
|
||||
PaymentProcessing.add_payment_transaction(tx)
|
||||
reference_id = int(data.get('customParameters', {}).get('SHOPPER_hyperpay_token_reference_id', 0))
|
||||
reference_model = data.get('customParameters', {}).get('SHOPPER_hyperpay_token_reference_model', '')
|
||||
if reference_id and reference_model and reference_model in request.env:
|
||||
record_id = request.env[reference_model].search([('id', '=', reference_id)])
|
||||
if record_id and hasattr(record_id, '_post_process_card_tokenization'):
|
||||
record_id._post_process_card_tokenization(token_id)
|
||||
return True
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import payment
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
from odoo import models, fields
|
||||
from odoo.http import request
|
||||
from odoo.addons.payment.models.payment_acquirer import PaymentToken
|
||||
import requests
|
||||
import re
|
||||
import dateutil
|
||||
import pytz
|
||||
|
||||
TEST_URL = "https://eu-test.oppwa.com"
|
||||
LIVE_URL = "https://eu-prod.oppwa.com"
|
||||
|
||||
|
||||
|
||||
class AcquirerHyperPay(models.Model):
|
||||
_inherit = 'payment.acquirer'
|
||||
|
||||
hyperpay_s2s_entity_id = fields.Char('Server2Server Entity Id', groups='base.group_user')
|
||||
|
||||
class HyperPayTransaction(models.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
def hyperpay_s2s_do_transaction(self, **kwargs):
|
||||
self.ensure_one()
|
||||
|
||||
if self.acquirer_id.state == 'test':
|
||||
domain = TEST_URL
|
||||
else:
|
||||
domain = LIVE_URL
|
||||
|
||||
url = f"{domain}/v1/registrations/{self.payment_token_id.acquirer_ref}/payments"
|
||||
|
||||
payload = self._hyperpay_get_s2s_transaction_payload(kwargs)
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.acquirer_id.hyperpay_authorization}"
|
||||
}
|
||||
response = requests.post(url, data=payload, headers=headers)
|
||||
data = response.json()
|
||||
return self._hyperpay_s2s_validate_transaction(data)
|
||||
|
||||
def _hyperpay_get_s2s_transaction_payload(self, data):
|
||||
partner_id = self.env.user.partner_id
|
||||
base_url = request.httprequest.host_url
|
||||
lang_code = str(self.env['res.lang'].search([('code', '=', self.env.user.lang)]).iso_code or '').upper()
|
||||
payload = {
|
||||
"entityId": self.acquirer_id.hyperpay_s2s_entity_id,
|
||||
"amount": '%.2f' % self.amount,
|
||||
"currency": self.currency_id.name,
|
||||
'paymentBrand': self.payment_token_id.hyperpay_payment_brand,
|
||||
"paymentType": "DB",
|
||||
'standingInstruction.mode': 'INITIAL',
|
||||
'standingInstruction.source': 'CIT',
|
||||
'standingInstruction.type': 'UNSCHEDULED',
|
||||
'shopperResultUrl': f'{base_url}hyperpay/tokens/result?transaction_id={self.id}',
|
||||
'notificationUrl': f'{base_url}hyperpay/tokens/result?transaction_id={self.id}',
|
||||
"billing.street1": partner_id.street or 'Riyadh',
|
||||
"billing.street2": partner_id.street2 or '',
|
||||
"billing.city": partner_id.city or 'Riyadh',
|
||||
"billing.state": partner_id.state_id.name or 'Riyadh',
|
||||
"billing.postcode": partner_id.zip or '',
|
||||
"billing.country": partner_id.country_id.code or 'SA',
|
||||
"customer.givenName": partner_id.name,
|
||||
"customer.surname": '',
|
||||
"customer.email": partner_id.email,
|
||||
"customer.mobile": partner_id.mobile or partner_id.phone or '',
|
||||
"customer.phone": partner_id.phone or partner_id.mobile or '',
|
||||
'customer.ip': request.httprequest.environ["REMOTE_ADDR"],
|
||||
'customer.language': lang_code,
|
||||
}
|
||||
if self.type != 'validation':
|
||||
payload.update({
|
||||
'standingInstruction.mode': 'REPEATED',
|
||||
'standingInstruction.source': 'MIT',
|
||||
'standingInstruction.initialTransactionId': self.payment_token_id.hyperpay_initial_transaction_id
|
||||
})
|
||||
return payload
|
||||
|
||||
def hyperpay_s2s_do_refund(self, **kwargs):
|
||||
self.ensure_one()
|
||||
|
||||
if self.acquirer_id.state == 'test':
|
||||
domain = TEST_URL
|
||||
else:
|
||||
domain = LIVE_URL
|
||||
|
||||
url = f"{domain}/v1/payments/{self.acquirer_reference}"
|
||||
base_url = request.httprequest.host_url
|
||||
|
||||
|
||||
payload = {
|
||||
'entityId': self.acquirer_id.hyperpay_s2s_entity_id,
|
||||
'paymentBrand': self.payment_token_id.hyperpay_payment_brand,
|
||||
'paymentType': 'RF',
|
||||
'amount': '%.2f' % self.amount,
|
||||
'currency': self.currency_id.name,
|
||||
'shopperResultUrl': f'{base_url}hyperpay/tokens/result',
|
||||
}
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.acquirer_id.hyperpay_authorization}"
|
||||
}
|
||||
response = requests.post(url, data=payload, headers=headers)
|
||||
data = response.json()
|
||||
return self._hyperpay_s2s_validate_transaction(data)
|
||||
|
||||
def _hyperpay_s2s_validate_transaction(self, data):
|
||||
success_re = r"^(000\.000\.|000\.100\.1|000\.[36]|000\.400\.1[12]0|000\.400\.0[^3]|000\.400\.100)"
|
||||
pending_re = r"^(000\.200|800\.400\.5|100\.400\.500)"
|
||||
|
||||
result = data.get('result')
|
||||
result_code = result.get('code')
|
||||
res = {
|
||||
'acquirer_reference': data.get('id'),
|
||||
'state_message': result.get('description', '')
|
||||
}
|
||||
|
||||
if re.match(success_re, result_code):
|
||||
date_validate = dateutil.parser.parse(data.get('timestamp')).astimezone(pytz.utc).replace(tzinfo=None)
|
||||
res.update(date=date_validate)
|
||||
if self.type == 'validation' and not self.payment_token_id.verified:
|
||||
self.payment_token_id.write({
|
||||
'verified': True,
|
||||
'hyperpay_initial_transaction_id': data.get('id', '')
|
||||
})
|
||||
self.payment_token_id.verified = True
|
||||
self._set_transaction_done()
|
||||
elif re.match(pending_re, result_code):
|
||||
self._set_transaction_pending()
|
||||
else:
|
||||
self._set_transaction_error(result.get('description', ''))
|
||||
return self.write(res)
|
||||
|
||||
|
||||
class HyperPayToken(models.Model):
|
||||
_inherit = 'payment.token'
|
||||
|
||||
hyperpay_payment_brand = fields.Char('Payment Brand')
|
||||
hyperpay_initial_transaction_id = fields.Char()
|
||||
|
||||
|
||||
PaymentToken.VALIDATION_AMOUNTS.update({
|
||||
'SAR': 5.00
|
||||
})
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="payment_acquirer_view_form" model="ir.ui.view">
|
||||
<field name="name">payment.acquirer.view.form</field>
|
||||
<field name="model">payment.acquirer</field>
|
||||
<field name="inherit_id" ref="payment.acquirer_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="hyperpay_merchant_id" position="after">
|
||||
|
||||
<field name="hyperpay_s2s_entity_id" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
from . import controllers
|
||||
from . import models
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
'name': 'Recurring Donation Payment',
|
||||
'version': '1.0',
|
||||
'description': '',
|
||||
'summary': '',
|
||||
'author': 'Abdul Rahman Saber',
|
||||
'website': '',
|
||||
'license': 'LGPL-3',
|
||||
'category': '',
|
||||
'depends': [
|
||||
'base', 'ensan_donation_request', 'payment_hyperpay_tokenization'
|
||||
],
|
||||
'auto_install': True,
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import main
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
from odoo.http import request
|
||||
|
||||
from odoo.addons.payment_hyperpay_tokenization.controllers.main import HyperPayTokenization
|
||||
|
||||
class RecurringDonationTokenization(HyperPayTokenization):
|
||||
|
||||
def _save_session_data(self, data):
|
||||
try:
|
||||
product_id = data.get('product_id')
|
||||
amount = data.get('amount')
|
||||
sms_status = data.get('sms_status')
|
||||
frequency = data.get('frequency')
|
||||
|
||||
if not all([product_id, amount, frequency]):
|
||||
return {'state': False, 'message': 'invalid payload'}
|
||||
|
||||
recurring_donation_id = request.env['donation.recurring'].sudo().create({
|
||||
'recurring_line_ids': [(0, 0, {
|
||||
'product_id': product_id,
|
||||
'quantity': 1,
|
||||
'price_unit': amount,
|
||||
})],
|
||||
'partner_id': request.env.user.partner_id.id,
|
||||
'frequency': frequency,
|
||||
'send_recurring_sms': sms_status
|
||||
})
|
||||
request.session['hyperpay_token_reference_id'] = recurring_donation_id.id
|
||||
request.session['hyperpay_token_reference_model'] = recurring_donation_id._name
|
||||
return True
|
||||
except Exception as e:
|
||||
request.env.cr.rollback()
|
||||
raise e
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import recurring_donation
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
from odoo import models, fields, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
class RecurringDonation(models.Model):
|
||||
_inherit = 'donation.recurring'
|
||||
|
||||
preferred_payment_token_id = fields.Many2one('payment.token', domain="[('partner_id', '=', partner_id)]")
|
||||
|
||||
def _post_process_card_tokenization(self, token_id):
|
||||
self.write({
|
||||
'preferred_payment_token_id': token_id.id
|
||||
})
|
||||
if token_id.verified:
|
||||
self.action_activate()
|
||||
|
||||
def _recurring_confirm_sale_order(self, order):
|
||||
transaction_id = self._create_payment_transaction(order)
|
||||
if transaction_id.state == 'done':
|
||||
return super()._recurring_confirm_sale_order(order)
|
||||
return False
|
||||
|
||||
def _create_payment_transaction(self, order):
|
||||
payment_token_id = self._get_recurring_payment_token_id()
|
||||
if not payment_token_id:
|
||||
raise ValidationError(_('This partner doesn\'t have registered payment tokens'))
|
||||
vals = {
|
||||
'type': 'form_save',
|
||||
'payment_token_id': payment_token_id
|
||||
}
|
||||
return order._create_payment_transaction(vals)
|
||||
|
||||
def _get_recurring_payment_token_id(self):
|
||||
self.ensure_one()
|
||||
if self.preferred_payment_token_id:
|
||||
return self.preferred_payment_token_id.id
|
||||
|
||||
return self.env['payment.token'].sudo().search([('partner_id', '=', self.partner_id.id), ('verified', '=', True)], limit=1).id
|
||||
Loading…
Reference in New Issue