diff --git a/packages/models-library/src/models_library/payments.py b/packages/models-library/src/models_library/payments.py index 22e58804526..5a043321e3b 100644 --- a/packages/models-library/src/models_library/payments.py +++ b/packages/models-library/src/models_library/payments.py @@ -1,11 +1,13 @@ from decimal import Decimal -from typing import Any, ClassVar +from typing import Any, ClassVar, TypeAlias from models_library.emails import LowerCaseEmailStr from pydantic import BaseModel, Field, validator from .products import StripePriceID, StripeTaxRateID +StripeInvoiceID: TypeAlias = str + class UserInvoiceAddress(BaseModel): line1: str | None = None diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/4a0f4efe8c86_add_invoice_id_column.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/4a0f4efe8c86_add_invoice_id_column.py new file mode 100644 index 00000000000..01a403733ef --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/4a0f4efe8c86_add_invoice_id_column.py @@ -0,0 +1,30 @@ +"""add invoice id column + +Revision ID: 4a0f4efe8c86 +Revises: 5b37c3bc99af +Create Date: 2024-03-21 10:27:45.493789+00:00 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "4a0f4efe8c86" +down_revision = "5b37c3bc99af" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "payments_transactions", + sa.Column("stripe_invoice_id", sa.String(), nullable=True), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("payments_transactions", "stripe_invoice_id") + # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py b/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py index 9f8a780033f..23e88d47fe6 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/payments_transactions.py @@ -95,6 +95,12 @@ def is_acknowledged(self) -> bool: nullable=True, doc="Link to invoice of this transaction. Available when completed", ), + sa.Column( + "stripe_invoice_id", + sa.String, + nullable=True, + doc="Invoice ID of invoice of this transaction. Available when completed", + ), # # States # diff --git a/services/payments/src/simcore_service_payments/api/rpc/_payments.py b/services/payments/src/simcore_service_payments/api/rpc/_payments.py index de5f4064a45..9e0939a4bea 100644 --- a/services/payments/src/simcore_service_payments/api/rpc/_payments.py +++ b/services/payments/src/simcore_service_payments/api/rpc/_payments.py @@ -19,7 +19,6 @@ from servicelib.logging_utils import get_log_record_extra, log_context from servicelib.rabbitmq import RPCRouter -from ...core.settings import ApplicationSettings from ...db.payments_transactions_repo import PaymentsTransactionsRepo from ...services import payments from ...services.payments_gateway import PaymentsGatewayApi @@ -47,9 +46,6 @@ async def init_payment( # pylint: disable=too-many-arguments stripe_tax_rate_id: StripeTaxRateID, comment: str | None = None, ) -> WalletPaymentInitiated: - - settings: ApplicationSettings = app.state.settings - with log_context( _logger, logging.INFO, @@ -60,7 +56,6 @@ async def init_payment( # pylint: disable=too-many-arguments return await payments.init_one_time_payment( gateway=PaymentsGatewayApi.get_from_app_state(app), repo=PaymentsTransactionsRepo(db_engine=app.state.engine), - settings=settings, amount_dollars=amount_dollars, target_credits=target_credits, product_name=product_name, diff --git a/services/payments/src/simcore_service_payments/api/rpc/_payments_methods.py b/services/payments/src/simcore_service_payments/api/rpc/_payments_methods.py index 202b3a69e7b..360dcf962c0 100644 --- a/services/payments/src/simcore_service_payments/api/rpc/_payments_methods.py +++ b/services/payments/src/simcore_service_payments/api/rpc/_payments_methods.py @@ -21,7 +21,6 @@ from servicelib.logging_utils import get_log_record_extra, log_context from servicelib.rabbitmq import RPCRouter -from ...core.settings import ApplicationSettings from ...db.payments_methods_repo import PaymentsMethodsRepo from ...db.payments_transactions_repo import PaymentsTransactionsRepo from ...services import payments, payments_methods @@ -170,8 +169,6 @@ async def pay_with_payment_method( # noqa: PLR0913 # pylint: disable=too-many-a stripe_tax_rate_id: StripeTaxRateID, comment: str | None = None, ): - settings: ApplicationSettings = app.state.settings - with log_context( _logger, logging.INFO, @@ -186,7 +183,6 @@ async def pay_with_payment_method( # noqa: PLR0913 # pylint: disable=too-many-a repo_transactions=PaymentsTransactionsRepo(db_engine=app.state.engine), repo_methods=PaymentsMethodsRepo(db_engine=app.state.engine), notifier=NotifierService.get_from_app_state(app), - settings=settings, payment_method_id=payment_method_id, amount_dollars=amount_dollars, target_credits=target_credits, diff --git a/services/payments/src/simcore_service_payments/db/payments_transactions_repo.py b/services/payments/src/simcore_service_payments/db/payments_transactions_repo.py index 3ed1a79bf6c..f4cedf361d0 100644 --- a/services/payments/src/simcore_service_payments/db/payments_transactions_repo.py +++ b/services/payments/src/simcore_service_payments/db/payments_transactions_repo.py @@ -8,6 +8,7 @@ PaymentNotFoundError, ) from models_library.api_schemas_webserver.wallets import PaymentID +from models_library.payments import StripeInvoiceID from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import WalletID @@ -66,6 +67,7 @@ async def update_ack_payment_transaction( completion_state: PaymentTransactionState, state_message: str | None, invoice_url: HttpUrl | None, + stripe_invoice_id: StripeInvoiceID | None, ) -> PaymentsTransactionsDB: """ - ACKs payment by updating state with SUCCESS, CANCEL, etc @@ -112,6 +114,7 @@ async def update_ack_payment_transaction( completed_at=sa.func.now(), state=completion_state, invoice_url=invoice_url, + stripe_invoice_id=stripe_invoice_id, **optional, ) .where(payments_transactions.c.payment_id == f"{payment_id}") diff --git a/services/payments/src/simcore_service_payments/models/db.py b/services/payments/src/simcore_service_payments/models/db.py index be7f858bfe2..98a28eeb77e 100644 --- a/services/payments/src/simcore_service_payments/models/db.py +++ b/services/payments/src/simcore_service_payments/models/db.py @@ -4,6 +4,7 @@ from models_library.api_schemas_webserver.wallets import PaymentID, PaymentMethodID from models_library.emails import LowerCaseEmailStr +from models_library.payments import StripeInvoiceID from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import WalletID @@ -23,6 +24,7 @@ "wallet_id": 123, "comment": "This is a test comment.", "invoice_url": None, + "stripe_invoice_id": None, "initiated_at": "2023-09-27T10:00:00", "state": PaymentTransactionState.PENDING, } @@ -38,6 +40,7 @@ class PaymentsTransactionsDB(BaseModel): wallet_id: WalletID comment: str | None invoice_url: HttpUrl | None + stripe_invoice_id: StripeInvoiceID | None initiated_at: datetime.datetime completed_at: datetime.datetime | None state: PaymentTransactionState diff --git a/services/payments/src/simcore_service_payments/services/auto_recharge_process_message.py b/services/payments/src/simcore_service_payments/services/auto_recharge_process_message.py index c1e3ec5557f..b2aedf83871 100644 --- a/services/payments/src/simcore_service_payments/services/auto_recharge_process_message.py +++ b/services/payments/src/simcore_service_payments/services/auto_recharge_process_message.py @@ -138,8 +138,6 @@ async def _perform_auto_recharge( payment_method_db: PaymentsMethodsDB, wallet_auto_recharge: GetWalletAutoRecharge, ): - settings: ApplicationSettings = app.state.settings - rabbitmq_rpc_client = get_rabbitmq_rpc_client(app) result = await rabbitmq_rpc_client.request( @@ -157,7 +155,6 @@ async def _perform_auto_recharge( repo_transactions=PaymentsTransactionsRepo(db_engine=app.state.engine), repo_methods=PaymentsMethodsRepo(db_engine=app.state.engine), notifier=NotifierService.get_from_app_state(app), - settings=settings, # payment_method_id=cast(PaymentMethodID, wallet_auto_recharge.payment_method_id), amount_dollars=wallet_auto_recharge.top_up_amount_in_usd, diff --git a/services/payments/src/simcore_service_payments/services/payments.py b/services/payments/src/simcore_service_payments/services/payments.py index b3219c95e87..58bf021ccc7 100644 --- a/services/payments/src/simcore_service_payments/services/payments.py +++ b/services/payments/src/simcore_service_payments/services/payments.py @@ -36,7 +36,6 @@ from tenacity.stop import stop_after_attempt from .._constants import RUT -from ..core.settings import ApplicationSettings from ..db.payments_transactions_repo import PaymentsTransactionsRepo from ..models.db import PaymentsTransactionsDB from ..models.db_to_api import to_payments_api_model @@ -58,7 +57,6 @@ async def init_one_time_payment( gateway: PaymentsGatewayApi, repo: PaymentsTransactionsRepo, - settings: ApplicationSettings, *, amount_dollars: Decimal, target_credits: Decimal, @@ -91,7 +89,6 @@ async def init_one_time_payment( else StripeTaxExempt.reverse ), ), - payment_gateway_tax_feature_enabled=settings.PAYMENTS_GATEWAY_TAX_FEATURE_ENABLED, ) submission_link = gateway.get_form_payment_url(init.payment_id) @@ -146,6 +143,7 @@ async def cancel_one_time_payment( completion_state=PaymentTransactionState.CANCELED, state_message=payment_cancelled.message, invoice_url=None, + stripe_invoice_id=None, ) @@ -164,6 +162,7 @@ async def acknowledge_one_time_payment( ), state_message=ack.message, invoice_url=ack.invoice_url, + stripe_invoice_id=ack.stripe_invoice_id, ) @@ -217,7 +216,6 @@ async def pay_with_payment_method( # noqa: PLR0913 repo_transactions: PaymentsTransactionsRepo, repo_methods: PaymentsMethodsRepo, notifier: NotifierService, - settings: ApplicationSettings, *, payment_method_id: PaymentMethodID, amount_dollars: Decimal, @@ -256,7 +254,6 @@ async def pay_with_payment_method( # noqa: PLR0913 else StripeTaxExempt.reverse ), ), - payment_gateway_tax_feature_enabled=settings.PAYMENTS_GATEWAY_TAX_FEATURE_ENABLED, ) payment_id = ack.payment_id @@ -290,6 +287,7 @@ async def pay_with_payment_method( # noqa: PLR0913 ), state_message=ack.message, invoice_url=ack.invoice_url, + stripe_invoice_id=ack.stripe_invoice_id, ) # NOTE: notifications here are done as background-task after responding `POST /wallets/{wallet_id}/payments-methods/{payment_method_id}:pay` diff --git a/services/payments/src/simcore_service_payments/services/payments_gateway.py b/services/payments/src/simcore_service_payments/services/payments_gateway.py index f10919c3658..0b1097492c6 100644 --- a/services/payments/src/simcore_service_payments/services/payments_gateway.py +++ b/services/payments/src/simcore_service_payments/services/payments_gateway.py @@ -121,25 +121,10 @@ class PaymentsGatewayApi( # @_handle_status_errors - async def init_payment( - self, payment: InitPayment, payment_gateway_tax_feature_enabled: bool - ) -> PaymentInitiated: - _payment_json = payment.dict( - exclude_none=True, - by_alias=True, - exclude={ - "user_address", - "stripe_price_id", - "stripe_tax_rate_id", - "stripe_tax_exempt_value", - }, - ) - if payment_gateway_tax_feature_enabled: - _payment_json = payment.dict(exclude_none=True, by_alias=True) - + async def init_payment(self, payment: InitPayment) -> PaymentInitiated: response = await self.client.post( "/init", - json=jsonable_encoder(_payment_json), + json=jsonable_encoder(payment.dict(exclude_none=True, by_alias=True)), ) response.raise_for_status() return PaymentInitiated.parse_obj(response.json()) @@ -210,24 +195,10 @@ async def pay_with_payment_method( self, id_: PaymentMethodID, payment: InitPayment, - payment_gateway_tax_feature_enabled: bool, # noqa: FBT001 ) -> AckPaymentWithPaymentMethod: - _payment_json = payment.dict( - exclude_none=True, - by_alias=True, - exclude={ - "user_address", - "stripe_price_id", - "stripe_tax_rate_id", - "stripe_tax_exempt_value", - }, - ) - if payment_gateway_tax_feature_enabled: - _payment_json = payment.dict(exclude_none=True, by_alias=True) - response = await self.client.post( f"/payment-methods/{id_}:pay", - json=jsonable_encoder(_payment_json), + json=jsonable_encoder(payment.dict(exclude_none=True, by_alias=True)), ) response.raise_for_status() return AckPaymentWithPaymentMethod.parse_obj(response.json()) diff --git a/services/payments/tests/unit/test_db_payments_transactions_repo.py b/services/payments/tests/unit/test_db_payments_transactions_repo.py index b5c086ddc43..177d2628a9e 100644 --- a/services/payments/tests/unit/test_db_payments_transactions_repo.py +++ b/services/payments/tests/unit/test_db_payments_transactions_repo.py @@ -73,6 +73,7 @@ async def test_one_time_payment_annotations_workflow(app: FastAPI): payment_id=fake.payment_id, completion_state=PaymentTransactionState.SUCCESS, invoice_url=fake.invoice_url, + stripe_invoice_id=fake.stripe_invoice_id, state_message="DONE", ) diff --git a/services/payments/tests/unit/test_services_payments.py b/services/payments/tests/unit/test_services_payments.py index b9a82f8fb4b..e195ec833a0 100644 --- a/services/payments/tests/unit/test_services_payments.py +++ b/services/payments/tests/unit/test_services_payments.py @@ -113,15 +113,12 @@ async def test_fails_to_pay_with_payment_method_without_funds( # Mocker providers notifier = NotifierService(mock_email_provider, mock_ws_provider) - settings = app.state.settings - payment = await payments.pay_with_payment_method( gateway=PaymentsGatewayApi.get_from_app_state(app), rut=rut, repo_transactions=PaymentsTransactionsRepo(db_engine=app.state.engine), repo_methods=PaymentsMethodsRepo(db_engine=app.state.engine), notifier=notifier, - settings=settings, # payment_method_id=payment_method_without_funds.payment_method_id, amount_dollars=100, diff --git a/services/payments/tests/unit/test_services_payments_gateway.py b/services/payments/tests/unit/test_services_payments_gateway.py index 1414d17fd1a..b9b1f183483 100644 --- a/services/payments/tests/unit/test_services_payments_gateway.py +++ b/services/payments/tests/unit/test_services_payments_gateway.py @@ -112,7 +112,6 @@ async def test_one_time_payment_workflow( stripe_tax_rate_id=faker.word(), stripe_tax_exempt_value=StripeTaxExempt.none, ), - payment_gateway_tax_feature_enabled=True, ) # form url @@ -192,7 +191,6 @@ async def test_payment_methods_workflow( stripe_tax_rate_id=faker.word(), stripe_tax_exempt_value=StripeTaxExempt.none, ), - payment_gateway_tax_feature_enabled=True, ) assert payment_with_payment_method.success diff --git a/services/payments/tests/unit/test_services_payments_gateway_with_tax_feature_disabled.py b/services/payments/tests/unit/test_services_payments_gateway_with_tax_feature_disabled.py index cbc384f5848..55508e5b3a8 100644 --- a/services/payments/tests/unit/test_services_payments_gateway_with_tax_feature_disabled.py +++ b/services/payments/tests/unit/test_services_payments_gateway_with_tax_feature_disabled.py @@ -335,7 +335,6 @@ async def test_payment_methods_workflow_with_tax_feature_disabled( stripe_tax_rate_id=faker.word(), stripe_tax_exempt_value=StripeTaxExempt.none, ), - payment_gateway_tax_feature_enabled=settings.PAYMENTS_GATEWAY_TAX_FEATURE_ENABLED, ) assert payment_with_payment_method.success @@ -365,7 +364,6 @@ async def test_one_time_payment_workflow_with_tax_feature_disabled( stripe_tax_rate_id=faker.word(), stripe_tax_exempt_value=StripeTaxExempt.none, ), - payment_gateway_tax_feature_enabled=settings.PAYMENTS_GATEWAY_TAX_FEATURE_ENABLED, ) # form url