-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by lmignon
- Loading branch information
Showing
82 changed files
with
2,760 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# generated from manifests external_dependencies | ||
extendable-pydantic>=1.2.0 | ||
fastapi | ||
pydantic>=2.0.0 | ||
pyjwt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# addons listed in this file are ignored by | ||
# setuptools-odoo-make-default (one addon per line) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
To learn more about this directory, please visit | ||
https://pypi.python.org/pypi/setuptools-odoo |
1 change: 1 addition & 0 deletions
1
setup/shopinvader_api_payment/odoo/addons/shopinvader_api_payment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../shopinvader_api_payment |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
1 change: 1 addition & 0 deletions
1
setup/shopinvader_api_payment_cart/odoo/addons/shopinvader_api_payment_cart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../shopinvader_api_payment_cart |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
1 change: 1 addition & 0 deletions
1
...opinvader_api_payment_provider_custom/odoo/addons/shopinvader_api_payment_provider_custom
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../shopinvader_api_payment_provider_custom |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
1 change: 1 addition & 0 deletions
1
...p/shopinvader_api_payment_provider_sips/odoo/addons/shopinvader_api_payment_provider_sips
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../shopinvader_api_payment_provider_sips |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
1 change: 1 addition & 0 deletions
1
...opinvader_api_payment_provider_stripe/odoo/addons/shopinvader_api_payment_provider_stripe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../shopinvader_api_payment_provider_stripe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import models | ||
from . import routers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Copyright 2024 ACSONE SA/NV | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
{ | ||
"name": "Shopinvader Api Payment", | ||
"summary": """ | ||
Shopinvader services to be able to pay (invoices, carts,...)""", | ||
"version": "16.0.1.0.0", | ||
"license": "AGPL-3", | ||
"author": "ACSONE SA/NV,Shopinvader", | ||
"website": "https://github.com/shopinvader/odoo-shopinvader-payment", | ||
"depends": [ | ||
"fastapi", | ||
"payment", | ||
"payment_sips", | ||
"pydantic", | ||
"extendable", | ||
"extendable_fastapi", | ||
], | ||
"external_dependencies": { | ||
"python": [ | ||
"fastapi", | ||
"pydantic>=2.0.0", | ||
"extendable-pydantic>=1.2.0", | ||
"pyjwt", | ||
] | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import payment_transaction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Copyright 2024 ACSONE SA/NV | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
from odoo import fields, models | ||
|
||
|
||
class PaymentTransaction(models.Model): | ||
_inherit = "payment.transaction" | ||
|
||
shopinvader_frontend_redirect_url = fields.Char( | ||
string="Shopinvader Frontend Redirect URL", | ||
help="URL where the frontend should be redirected after the payment processing", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* Marie Lejeune <[email protected]> | ||
* Stéphane Bidoul <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
This addon is the core of the new Shopinvader API Payment addons suite. | ||
It defines basic services, which will be extended in two axes. | ||
|
||
* The first axe concerns the payable object. Here the methods should work with any abstract payable object (sale order, account invoice, ...) but specific logic must be implemented in related addons (see `shopinvader_api_payment_cart` to pay sale orders for eg.) | ||
* The second axe concerns the payment provider. The idea is to develop one addon for each payment provider. Some of them are already available, see `shopinvader_api_payment_sips`, `shopinvader_api_payment_stripe`, `shopinvader_api_payment_custom`. In these addons we add the necessary logic to redirect to the payment provider payment website, the return url ... | ||
|
||
All payment routes are public. We must thus encode all sensitive info. | ||
The `Payable` object achieves this. In each service we ensure that the payable | ||
wasn't tampered. | ||
|
||
**Concrete Usage** | ||
|
||
The idea to use this suite of addons is the following. Assume you have a valid | ||
payable (see addons of the first axe on how to get them, for eg. `shopinvader_api_payment_cart` | ||
on how to get the payable of the current cart). | ||
|
||
1. Get all providers that are allowed to pay your payable object. | ||
You just need to call the GET route `/payment/methods` with your payable for this. | ||
|
||
2. Once you chose the payment method you want to use, create the payment transaction | ||
calling the POST route `/payment/transactions` with your payable + some | ||
additional input info (the chosen provider, the frontend redirect url...). | ||
See the associated `TransactionCreate` Pydantic schema. | ||
|
||
3. The following (and last) step depends on the chosen provider. See more info | ||
into the dedicated Shopinvader API payment addon. | ||
However, the idea is often the same: a `redirect_form_html` is returned and | ||
you should submit this HTML form to call the provider services. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .payment import payment_router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
# Copyright Odoo SA (https://odoo.com) | ||
# Copyright 2024 ACSONE SA (https://acsone.eu). | ||
# @author Stéphane Bidoul <[email protected]> | ||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
import logging | ||
from typing import Annotated, Any | ||
from urllib.parse import urljoin | ||
|
||
from fastapi import APIRouter, Depends, HTTPException, Request | ||
|
||
from odoo import api, models | ||
|
||
from odoo.addons.fastapi.dependencies import odoo_env | ||
from odoo.addons.payment.models.payment_provider import PaymentProvider | ||
from odoo.addons.payment.models.payment_transaction import PaymentTransaction | ||
|
||
from ..schemas import ( | ||
PaymentDataWithMethods, | ||
TransactionCreate, | ||
TransactionProcessingValues, | ||
) | ||
from ..schemas import ( | ||
PaymentProvider as PaymentProviderSchema, | ||
) | ||
from .utils import Payable | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
payment_router = APIRouter(tags=["payment"]) | ||
|
||
|
||
@payment_router.get("/payment/methods") | ||
def pay( | ||
payable: str, | ||
odoo_env: Annotated[api.Environment, Depends(odoo_env)], | ||
) -> PaymentDataWithMethods: | ||
"""Available payment providers for the given encoded payment data. | ||
This route is public, so it is possible to pay anonymously provided that the | ||
parameters are obtained securely by another mean. An authenticated user can | ||
obtain the parameters with corresponding routes on the related payable | ||
objects (/cart/current/payable for e.g.). | ||
""" | ||
try: | ||
payable_obj = Payable.decode(odoo_env, payable) | ||
except Exception as e: | ||
_logger.info("Could not decode payable") | ||
raise HTTPException(403) from e | ||
# This method is similar to Odoo's PaymentPortal.payment_pay | ||
providers_sudo = ( | ||
odoo_env["payment.provider"] | ||
.sudo() | ||
._get_compatible_providers( | ||
payable_obj.company_id, | ||
payable_obj.partner_id, | ||
payable_obj.amount, | ||
currency_id=payable_obj.currency_id, | ||
) | ||
) | ||
return PaymentDataWithMethods( | ||
payable=payable, | ||
payable_reference=payable_obj.payable_reference, | ||
amount=payable_obj.amount, | ||
currency_code=odoo_env["res.currency"].browse(payable_obj.currency_id).name, | ||
# We assume that the payable model has a currency field. | ||
# This shouldn't be a big assumption | ||
amount_formatted=odoo_env[payable_obj.payable_model] | ||
.sudo() | ||
.browse(payable_obj.payable_id) | ||
.currency_id.format(payable_obj.amount), | ||
providers=[ | ||
PaymentProviderSchema.from_payment_provider(provider) | ||
for provider in providers_sudo | ||
], | ||
) | ||
|
||
|
||
@payment_router.post("/payment/transactions") | ||
def transaction( | ||
data: TransactionCreate, | ||
request: Request, | ||
odoo_env: Annotated[api.Environment, Depends(odoo_env)], | ||
) -> TransactionProcessingValues: | ||
"""Create a payment transaction. | ||
Input is data obtained from /payment/providers, with the provider selected by the | ||
user. This route is public, so it is possible to pay anonymously. | ||
This route will automatically redirect to the return route linked to | ||
the specified provider. The user will finally land on data.frontend_redirect_url | ||
""" | ||
try: | ||
payable_obj = Payable.decode(odoo_env, data.payable) | ||
except Exception as e: | ||
_logger.info("Could not decode payable") | ||
raise HTTPException(403) from e | ||
# similar to Odoo's /payment/transaction route | ||
if data.flow == "redirect": | ||
providers_sudo = ( | ||
odoo_env["payment.provider"] | ||
.sudo() | ||
._get_compatible_providers( | ||
payable_obj.company_id, | ||
payable_obj.partner_id, | ||
payable_obj.amount, | ||
currency_id=payable_obj.currency_id, | ||
) | ||
) | ||
if not data.provider_id or data.provider_id not in providers_sudo.ids: | ||
_logger.info( | ||
"Invalid provider %s for partner %s", | ||
data.provider_id, | ||
payable_obj.partner_id, | ||
) | ||
raise HTTPException(403) | ||
provider_sudo = odoo_env["payment.provider"].sudo().browse(data.provider_id) | ||
|
||
# Create the transaction | ||
tx_sudo = odoo_env[ | ||
"shopinvader_api_payment.payment_router.helper" | ||
]._create_transaction(data, provider_sudo, request, odoo_env) | ||
tx_sudo._log_sent_message() | ||
|
||
transaction_processing_values = odoo_env[ | ||
"shopinvader_api_payment.payment_router.helper" | ||
]._get_tx_processing_values( | ||
tx_sudo, | ||
payable=data.payable, | ||
frontend_redirect_url=data.frontend_redirect_url, | ||
) | ||
return transaction_processing_values | ||
else: | ||
raise NotImplementedError("Only redirect flow is supported") | ||
|
||
|
||
class ShopinvaderApiPaymentRouterHelper(models.AbstractModel): | ||
_name = "shopinvader_api_payment.payment_router.helper" | ||
_description = "ShopInvader API Payment Router Helper" | ||
|
||
def _get_additional_transaction_create_values( | ||
self, | ||
data: TransactionCreate, | ||
odoo_env: Annotated[api.Environment, Depends(odoo_env)], | ||
) -> dict: | ||
# Intended to be extended for invoices, carts... | ||
additional_transaction_create_values = {} | ||
return additional_transaction_create_values | ||
|
||
def _get_tx_create_values( | ||
self, | ||
data: TransactionCreate, | ||
provider_sudo: PaymentProvider, | ||
odoo_env: Annotated[api.Environment, Depends(odoo_env)], | ||
) -> dict: | ||
try: | ||
payable_obj = Payable.decode(odoo_env, data.payable) | ||
except Exception as e: | ||
_logger.info("Could not decode payable") | ||
raise HTTPException(403) from e | ||
additional_transaction_create_values = ( | ||
self._get_additional_transaction_create_values(data, odoo_env) | ||
) | ||
|
||
is_validation = False # future | ||
# compute transaction reference from payable reference | ||
tx_reference = ( | ||
odoo_env["payment.transaction"] | ||
.sudo() | ||
._compute_reference( | ||
provider_code=provider_sudo.code, | ||
prefix=payable_obj.payable_reference, | ||
# TODO are custom_create_values and kwargs really needed | ||
# **(custom_create_values or {}), | ||
# **kwargs | ||
) | ||
) | ||
|
||
return { | ||
"provider_id": data.provider_id, | ||
"reference": tx_reference, | ||
"amount": payable_obj.amount, | ||
"currency_id": payable_obj.currency_id, | ||
"partner_id": payable_obj.partner_id, | ||
"shopinvader_frontend_redirect_url": data.frontend_redirect_url, | ||
# 'token_id': token_id, | ||
"operation": f"online_{data.flow}" if not is_validation else "validation", | ||
"tokenize": False, | ||
**additional_transaction_create_values, | ||
} | ||
|
||
def _create_transaction( | ||
self, | ||
data: TransactionCreate, | ||
provider_sudo: PaymentProvider, | ||
request: Request, | ||
odoo_env: Annotated[api.Environment, Depends(odoo_env)], | ||
) -> dict: | ||
transaction_values = odoo_env[ | ||
"shopinvader_api_payment.payment_router.helper" | ||
]._get_tx_create_values(data, provider_sudo, odoo_env) | ||
tx_sudo = ( | ||
odoo_env["payment.transaction"] | ||
.sudo() | ||
.with_context( | ||
shopinvader_api_payment=True, | ||
shopinvader_api_payment_base_url=urljoin( | ||
str(request.url), "providers/" | ||
), | ||
) | ||
.create(transaction_values) | ||
) | ||
return tx_sudo | ||
|
||
def _get_tx_processing_values( | ||
self, tx_sudo: PaymentTransaction, **kwargs: Any | ||
) -> TransactionProcessingValues: | ||
""" | ||
Extract the creation of the response to allow to extend it. | ||
""" | ||
return TransactionProcessingValues( | ||
flow="redirect", **tx_sudo._get_processing_values() | ||
) |
Oops, something went wrong.