[ADD] base modules
This commit is contained in:
2
portal_stripe_payment/__init__.py
Executable file
2
portal_stripe_payment/__init__.py
Executable file
@@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
34
portal_stripe_payment/__manifest__.py
Executable file
34
portal_stripe_payment/__manifest__.py
Executable file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Stripe Integration for SaaS Portal",
|
||||
|
||||
'summary': """Stripe Payment Integration for SaaS Portal""",
|
||||
|
||||
'author': "CodeTuple Solutions",
|
||||
'website': "https://codetuple.io",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/13.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'Invoicing',
|
||||
'version': '14.0.0.0',
|
||||
"license": "OPL-1",
|
||||
# 'images': [
|
||||
# 'static/description/icon.png',
|
||||
# ],
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base', 'web', 'kk_odoo_saas', 'rest_api', 'sale_subscription'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/views.xml',
|
||||
'views/sale_subscription.xml',
|
||||
'views/account_move.xml',
|
||||
'views/res_partner.xml',
|
||||
# 'views/res_config_settings_views.xml',
|
||||
],
|
||||
"application": True,
|
||||
|
||||
}
|
||||
1
portal_stripe_payment/controllers/__init__.py
Executable file
1
portal_stripe_payment/controllers/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import stripe_payment
|
||||
96
portal_stripe_payment/controllers/stripe_payment.py
Executable file
96
portal_stripe_payment/controllers/stripe_payment.py
Executable file
@@ -0,0 +1,96 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from odoo.addons.rest_api.controllers.main import *
|
||||
from odoo import http, tools, _, SUPERUSER_ID
|
||||
import stripe
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StripePayment(http.Controller):
|
||||
@http.route('/api/stripe/create-checkout-session', methods=['POST'], type='http', csrf=False, auth='none', cors=rest_cors_value)
|
||||
@check_permissions
|
||||
def create_checkout_session(self, **kw):
|
||||
cr, uid = request.cr, request.session.uid
|
||||
api_key = request.env(cr, uid)['ir.config_parameter'].sudo().get_param('stripe_secret_api_key')
|
||||
saas_portal_url = request.env(cr, uid)['ir.config_parameter'].sudo().get_param('portal_url', 'https://saas.vercel.app')
|
||||
pscs = request.env(cr, uid)['portal.stripe.checkout.session']
|
||||
|
||||
if not api_key:
|
||||
return error_response_400__invalid_object_id()
|
||||
stripe.api_key = api_key
|
||||
try:
|
||||
body = json.loads(request.httprequest.data)
|
||||
except Exception as e:
|
||||
_logger.error(e)
|
||||
return error_response_400__invalid_object_id()
|
||||
|
||||
if body.get('plan_id'):
|
||||
|
||||
user = request.env(cr, uid)['res.users'].browse([uid])
|
||||
partner = user.partner_id
|
||||
|
||||
plan = request.env(cr, uid)['saas.package'].sudo().search([('id', '=', body.get('plan_id'))])
|
||||
if plan and plan.stripe_product_id:
|
||||
try:
|
||||
checkout_session = stripe.checkout.Session.create(
|
||||
line_items=[
|
||||
{
|
||||
'price': plan.stripe_product_id,
|
||||
'quantity': 1,
|
||||
},
|
||||
],
|
||||
client_reference_id=uid,
|
||||
mode='subscription',
|
||||
success_url=str(saas_portal_url) + '/payment/success?session_id={CHECKOUT_SESSION_ID}',
|
||||
cancel_url=str(saas_portal_url) + '/payment/canceled',
|
||||
customer_email=partner.email if partner.email and not partner.related_stripe_id else None,
|
||||
customer=partner.related_stripe_id if partner.related_stripe_id else None,
|
||||
)
|
||||
pscs.sudo().create({'name': datetime.now(), 'session_id': checkout_session.id, 'user_id': uid})
|
||||
return successful_response(200, {'redirect_url': checkout_session.url})
|
||||
except Exception as e:
|
||||
_logger.error(e)
|
||||
return error_response_400__invalid_object_id()
|
||||
|
||||
return error_response_400__invalid_object_id()
|
||||
|
||||
|
||||
|
||||
@http.route('/api/stripe/webhooks', methods=['POST'], type='json', csrf=False, auth='public', cors='*')
|
||||
# @check_permissions
|
||||
def stripe_webhook(self, **kw):
|
||||
event = None
|
||||
stripe_signature = request.httprequest.headers.get('Stripe-Signature')
|
||||
|
||||
cr, uid = request.cr, request.session.uid
|
||||
endpoint_secret = request.env(cr, uid)['ir.config_parameter'].sudo().get_param('stripe_endpoint_secret')
|
||||
|
||||
if not stripe_signature or not endpoint_secret:
|
||||
return json.dumps({'error': 'stripe signature or endpoint secret not found'})
|
||||
|
||||
payload = request.httprequest.data
|
||||
|
||||
try:
|
||||
event = stripe.Webhook.construct_event(payload, stripe_signature, endpoint_secret)
|
||||
|
||||
if event.get('type') == 'checkout.session.completed':
|
||||
pscs = request.env(cr, uid)['portal.stripe.checkout.session']
|
||||
pscs.with_delay().post_session_completion_tasks(session=json.loads(payload)) #<<<-------------
|
||||
# pscs.post_session_completion_tasks(session=json.loads(payload))
|
||||
|
||||
return json.dumps({'response': 'Success! Creating Subscription... '})
|
||||
|
||||
|
||||
except ValueError as e:
|
||||
# Invalid payload
|
||||
_logger.error(e)
|
||||
return json.dumps({'error': 'ValueError occurred'})
|
||||
except stripe.error.SignatureVerificationError as e:
|
||||
# Invalid signature
|
||||
_logger.error(e)
|
||||
return json.dumps({'error': 'Unable to verify Signature'})
|
||||
return json.dumps({'error': 'Unknown Error Occurred'})
|
||||
|
||||
4
portal_stripe_payment/models/__init__.py
Executable file
4
portal_stripe_payment/models/__init__.py
Executable file
@@ -0,0 +1,4 @@
|
||||
from . import models
|
||||
from . import subscription
|
||||
from . import account_move
|
||||
from . import res_partner
|
||||
10
portal_stripe_payment/models/account_move.py
Executable file
10
portal_stripe_payment/models/account_move.py
Executable file
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api, _
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
related_stripe_id = fields.Char(string="Related Stripe Invoice")
|
||||
43
portal_stripe_payment/models/models.py
Executable file
43
portal_stripe_payment/models/models.py
Executable file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StripeCheckoutSession(models.Model):
|
||||
_name = 'portal.stripe.checkout.session'
|
||||
name = fields.Char()
|
||||
user_id = fields.Many2one('res.users', 'Client User')
|
||||
session_id = fields.Char()
|
||||
session_completed = fields.Boolean(default=False)
|
||||
completion_payload = fields.Text()
|
||||
|
||||
def post_session_completion_tasks(self, session=None):
|
||||
if session is None:
|
||||
session = {}
|
||||
_logger.error('session is None, Exiting')
|
||||
|
||||
session_object = session.get('data', {'object': False}).get('object')
|
||||
if session_object:
|
||||
session_id = session_object.get('id')
|
||||
client_reference_id = session_object.get('client_reference_id')
|
||||
if session_id and client_reference_id:
|
||||
db_session = self.sudo().search([('session_id', '=', session_id), ('session_completed', '=', False),
|
||||
('user_id', '=', int(client_reference_id))], limit=1)
|
||||
if db_session: # <<<----------
|
||||
db_session.sudo().write({'completion_payload': str(session), 'session_completed': True})
|
||||
stripe_sub_id = session_object.get('subscription')
|
||||
if stripe_sub_id:
|
||||
self.env['sale.subscription'].sudo().create_from_stripe_api(user_id=client_reference_id,
|
||||
stripe_sub_id=stripe_sub_id)
|
||||
else:
|
||||
_logger.error('Unable to find stripe checkout session on db')
|
||||
else:
|
||||
_logger.error('Session id is not found, Exiting')
|
||||
else:
|
||||
_logger.error('session object not found, Exiting')
|
||||
|
||||
|
||||
10
portal_stripe_payment/models/res_partner.py
Executable file
10
portal_stripe_payment/models/res_partner.py
Executable file
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api, _
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
related_stripe_id = fields.Char()
|
||||
117
portal_stripe_payment/models/subscription.py
Executable file
117
portal_stripe_payment/models/subscription.py
Executable file
@@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
import stripe
|
||||
import logging
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SaleSubscription(models.Model):
|
||||
_inherit = 'sale.subscription'
|
||||
stripe_subscription_id = fields.Char(string="Related Stripe Subscription")
|
||||
|
||||
def create_from_stripe_api(self, user_id=False, stripe_sub_id=False):
|
||||
if not stripe_sub_id or not user_id:
|
||||
return
|
||||
stripe.api_key = self.env['ir.config_parameter'].sudo().get_param('stripe_secret_api_key')
|
||||
ssub = stripe.Subscription.retrieve(str(stripe_sub_id))
|
||||
if ssub:
|
||||
try:
|
||||
stripe_price_id = ssub.get('items').get('data')[0].get('price').get('id')
|
||||
if stripe_price_id:
|
||||
saas_package = self.env['saas.package'].sudo().search([('stripe_product_id', '=', stripe_price_id)])
|
||||
if saas_package and saas_package.subscription_template:
|
||||
self._create_from_stripe_api(user_id, saas_package, stripe_sub_id)
|
||||
else:
|
||||
_logger.error('Unable to find SaaS package or Subscription Template')
|
||||
else:
|
||||
_logger.error('Unable to find Stripe Price ID')
|
||||
|
||||
except Exception as e:
|
||||
_logger.error(str(e))
|
||||
else:
|
||||
_logger.error('Unable to find Stripe Subscription object')
|
||||
|
||||
def _create_from_stripe_api(self, user_id, package, stripe_sub_id):
|
||||
user = self.env['res.users'].sudo().search([('id', '=', user_id)], limit=1)
|
||||
if user and package and stripe_sub_id:
|
||||
new_sub = self.env['sale.subscription'].sudo().create({
|
||||
'partner_id': user.partner_id.id,
|
||||
'template_id': package.subscription_template.id,
|
||||
'stripe_subscription_id': stripe_sub_id,
|
||||
'recurring_invoice_line_ids':
|
||||
[(0, 0,
|
||||
{'product_id': package.year_product_id.id, 'name': package.name, 'price_unit': package.year_price,
|
||||
'uom_id': package.year_product_id.uom_id.id})]
|
||||
})
|
||||
started = new_sub.sudo().start_subscription()
|
||||
if started:
|
||||
try:
|
||||
invoice_action = new_sub.sudo().generate_recurring_invoice()
|
||||
invoice_id = invoice_action.get('res_id')
|
||||
if invoice_id:
|
||||
invoice_obj = self.env['account.move'].sudo().browse([invoice_id])
|
||||
if invoice_obj:
|
||||
invoice_obj.action_post()
|
||||
new_sub.sudo().create_saas_app_from_subscription(user_id=user_id, package=package)
|
||||
|
||||
# Payment = self.env['account.payment'].with_context(default_line_ids=invoice_obj.invoice_line_ids.ids, default_invoice_ids=[(4, invoice_id, False)])
|
||||
# print(Payment)
|
||||
# payment_vals = {
|
||||
# 'date': datetime.date.today(),
|
||||
# 'amount': invoice_obj.amount_total,
|
||||
# 'payment_type': 'inbound',
|
||||
# 'partner_type': 'customer',
|
||||
# 'partner_id': user.partner_id.id,
|
||||
# 'line_ids': invoice_obj.invoice_line_ids,
|
||||
# # 'ref': self.communication,
|
||||
# # 'journal_id': self.journal_id.id,
|
||||
# # 'currency_id': self.currency_id.id,
|
||||
# # 'partner_bank_id': self.partner_bank_id.id,
|
||||
# # 'payment_method_id': self.payment_method_id.id,
|
||||
# # 'destination_account_id': self.line_ids[0].account_id.id
|
||||
# }
|
||||
#
|
||||
# payment = Payment.sudo().create(payment_vals)
|
||||
# print(payment.action_post())
|
||||
|
||||
except UserError as e:
|
||||
_logger.error(e)
|
||||
else:
|
||||
_logger.error("Unable to start subscription")
|
||||
else:
|
||||
_logger.error('Stripe Subscription Id not found')
|
||||
|
||||
def create_saas_app_from_subscription(self, user_id=False, package=False):
|
||||
saas_app_env = self.env['kk_odoo_saas.app']
|
||||
|
||||
def_vals = saas_app_env.default_get(fields_list=['app_name'])
|
||||
|
||||
if user_id:
|
||||
def_vals['admin_user'] = user_id
|
||||
else:
|
||||
def_vals['admin_user'] = self.partner_id.user_ids.ids[0]
|
||||
app_name = def_vals.get('app_name')
|
||||
configurations = self.env["kk_odoo_saas.k8s.config"]
|
||||
config = configurations.get_default_config()
|
||||
if config:
|
||||
def_vals['configuration'] = config.id
|
||||
def_vals['sub_domain_name'] = app_name
|
||||
def_vals['subscription_id'] = self.id
|
||||
def_vals['client_db_name'] = app_name
|
||||
# def_vals['module_ids'] = [(6, 0, saas_app_ids)]
|
||||
if package:
|
||||
def_vals['docker_image'] = package.docker_image.id
|
||||
def_vals['name'] = '{}'.format(self.code)
|
||||
|
||||
saas_app = saas_app_env.create(def_vals)
|
||||
|
||||
self.build_id = saas_app.id
|
||||
|
||||
_logger.info('Going to Deploy SaaS App, Subscription is going to start')
|
||||
# saas_app.deploy_app()
|
||||
else:
|
||||
_logger.error('Cant create SaaS App, No K8s configuration found')
|
||||
2
portal_stripe_payment/security/ir.model.access.csv
Executable file
2
portal_stripe_payment/security/ir.model.access.csv
Executable file
@@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_saas_portal_api_stripe_session,access_saas_portal_api_stripe_session,model_portal_stripe_checkout_session,kk_odoo_saas.group_saas_manager,1,1,1,1
|
||||
|
BIN
portal_stripe_payment/static/description/icon.png
Executable file
BIN
portal_stripe_payment/static/description/icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
16
portal_stripe_payment/views/account_move.xml
Executable file
16
portal_stripe_payment/views/account_move.xml
Executable file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_move_form_inherit_stripe" model="ir.ui.view">
|
||||
<field name="name">account.move.form.inherit</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='payment_reference']" position="after">
|
||||
<field name="related_stripe_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
18
portal_stripe_payment/views/res_partner.xml
Executable file
18
portal_stripe_payment/views/res_partner.xml
Executable file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="res_partner_view_inherit_stripe_integ">
|
||||
<field name="name">partner.view.stripe.integration</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="category_id" position="after">
|
||||
<field name="related_stripe_id"/>
|
||||
</field>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
15
portal_stripe_payment/views/sale_subscription.xml
Executable file
15
portal_stripe_payment/views/sale_subscription.xml
Executable file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="sale_subscription_view_form" model="ir.ui.view">
|
||||
<field name="name">sale.subscription.inherit.form.view</field>
|
||||
<field name="model">sale.subscription</field>
|
||||
<field name="inherit_id" ref="sale_subscription.sale_subscription_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="stripe_subscription_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
34
portal_stripe_payment/views/views.xml
Executable file
34
portal_stripe_payment/views/views.xml
Executable file
@@ -0,0 +1,34 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="saas_portal_stripe_checkout_session" model="ir.actions.act_window">
|
||||
<field name="name">Stripe Checkout Session</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">portal.stripe.checkout.session</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<record id="stripe_checkout_session_view_form" model="ir.ui.view">
|
||||
<field name="name">saas_portal_stripe_view_form</field>
|
||||
<field name="model">portal.stripe.checkout.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<form >
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="session_id"/>
|
||||
<field name="session_completed"/>
|
||||
<field name="completion_payload"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Payment" id="saas_payment" parent="kk_odoo_saas.menu_root"/>
|
||||
<menuitem name="Stripe Checkout Sessions" id="portal_stripe_checkout_session"
|
||||
parent="saas_payment"
|
||||
action="saas_portal_stripe_checkout_session"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user