From 809a4d6324e018f4fcd94e7ec75981ea8ac8e3c0 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 30 Oct 2023 19:38:56 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Changes=20in=20auto-recharge=20limi?= =?UTF-8?q?t=20policy=20(=F0=9F=97=83=EF=B8=8F,=20=E2=9A=A0=EF=B8=8F)=20(#?= =?UTF-8?q?4946)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env-devel | 3 +- .../api_schemas_webserver/wallets.py | 16 +++---- .../src/models_library/invitations.py | 11 ++++- packages/postgres-database/VERSION | 2 +- packages/postgres-database/setup.cfg | 2 +- ...695487_rm_cols_in_payments_autorecharge.py | 46 +++++++++++++++++++ .../models/payments_autorecharge.py | 24 ++-------- .../utils_payments_autorecharge.py | 23 ++-------- .../tests/test_utils_payments_autorecharge.py | 38 +++------------ services/docker-compose.yml | 3 +- services/invitations/openapi.json | 6 +-- .../api/v0/openapi.yaml | 43 +++++++++-------- .../payments/_autorecharge_api.py | 30 +++++++----- .../payments/_autorecharge_db.py | 6 +-- .../payments/settings.py | 16 +++++-- .../wallets/payments/test_payments_methods.py | 43 +++++++++++------ 16 files changed, 173 insertions(+), 139 deletions(-) create mode 100644 packages/postgres-database/src/simcore_postgres_database/migration/versions/d0d544695487_rm_cols_in_payments_autorecharge.py diff --git a/.env-devel b/.env-devel index a7c894f0d7b..92e6fba5cd8 100644 --- a/.env-devel +++ b/.env-devel @@ -64,8 +64,9 @@ LOG_FORMAT_LOCAL_DEV_ENABLED=1 WEBSERVER_PAYMENTS={} PAYMENTS_ACCESS_TOKEN_EXPIRE_MINUTES=30 PAYMENTS_ACCESS_TOKEN_SECRET_KEY=2c0411810565e063309be1457009fb39ce023946f6a354e6935107b57676 -PAYMENTS_AUTORECHARGE_DEFAULT_MIN_BALANCE=20.0 +PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT=10000 PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT=100.0 +PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS=100 PAYMENTS_FAKE_COMPLETION_DELAY_SEC=10 PAYMENTS_FAKE_COMPLETION=0 PAYMENTS_GATEWAY_API_SECRET=replace-with-api-secret diff --git a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py index 62e38f0483c..e26c5c47118 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/wallets.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/wallets.py @@ -2,7 +2,7 @@ from decimal import Decimal from typing import Any, ClassVar, Literal, TypeAlias -from pydantic import Field, HttpUrl, PositiveInt +from pydantic import Field, HttpUrl from ..basic_types import IDStr, NonNegativeDecimal from ..users import GroupID @@ -171,23 +171,23 @@ class GetWalletAutoRecharge(OutputSchema): ..., description="Payment method in the wallet used to perform the auto-recharge payments or None if still undefined", ) - min_balance_in_usd: NonNegativeDecimal = Field( + min_balance_in_credits: NonNegativeDecimal = Field( ..., - description="Minimum balance in USD that triggers an auto-recharge", + description="Minimum balance in credits that triggers an auto-recharge [Read only]", ) top_up_amount_in_usd: NonNegativeDecimal = Field( ..., description="Amount in USD payed when auto-recharge condition is satisfied", ) - top_up_countdown: PositiveInt | None = Field( - default=None, - description="Maximum number of top-ups left or None to denote unlimited", + monthly_limit_in_usd: NonNegativeDecimal | None = Field( + ..., + description="Maximum amount in USD charged within a natural month." + "None indicates no limit.", ) class ReplaceWalletAutoRecharge(InputSchema): enabled: bool payment_method_id: PaymentMethodID - min_balance_in_usd: NonNegativeDecimal top_up_amount_in_usd: NonNegativeDecimal - top_up_countdown: PositiveInt | None + monthly_limit_in_usd: NonNegativeDecimal | None diff --git a/packages/models-library/src/models_library/invitations.py b/packages/models-library/src/models_library/invitations.py index 1b89004b81e..78e84b95556 100644 --- a/packages/models-library/src/models_library/invitations.py +++ b/packages/models-library/src/models_library/invitations.py @@ -1,6 +1,6 @@ from datetime import datetime -from pydantic import BaseModel, Field, PositiveInt +from pydantic import BaseModel, Field, PositiveInt, validator from .emails import LowerCaseEmailStr @@ -10,7 +10,7 @@ class InvitationInputs(BaseModel): issuer: str = Field( ..., - description="Identifies who issued the invitation. E.g. an email, a service name etc", + description="Identifies who issued the invitation. E.g. an email, a service name etc. NOTE: it will be trimmed if exceeds maximum", min_length=1, max_length=30, ) @@ -28,6 +28,13 @@ class InvitationInputs(BaseModel): description="If set, the account's primary wallet will add extra credits corresponding to this ammount in USD", ) + @validator("issuer", pre=True) + @classmethod + def trim_long_issuers_to_max_length(cls, v): + if v and isinstance(v, str): + return v[:29] + return v + class InvitationContent(InvitationInputs): """Data in an invitation""" diff --git a/packages/postgres-database/VERSION b/packages/postgres-database/VERSION index 78bc1abd14f..d9df1bbc0c7 100644 --- a/packages/postgres-database/VERSION +++ b/packages/postgres-database/VERSION @@ -1 +1 @@ -0.10.0 +0.11.0 diff --git a/packages/postgres-database/setup.cfg b/packages/postgres-database/setup.cfg index 2784c26578c..a8e21db92bb 100644 --- a/packages/postgres-database/setup.cfg +++ b/packages/postgres-database/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.10.0 +current_version = 0.11.0 commit = True message = packages/postgres-database version: {current_version} → {new_version} tag = False diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/d0d544695487_rm_cols_in_payments_autorecharge.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/d0d544695487_rm_cols_in_payments_autorecharge.py new file mode 100644 index 00000000000..cd6fbd4323c --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/d0d544695487_rm_cols_in_payments_autorecharge.py @@ -0,0 +1,46 @@ +"""rm cols in payments_autorecharge + +Revision ID: d0d544695487 +Revises: 2a4b4167e088 +Create Date: 2023-10-27 18:29:46.409910+00:00 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "d0d544695487" +down_revision = "2a4b4167e088" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "payments_autorecharge", + sa.Column("monthly_limit_in_usd", sa.Numeric(scale=2), nullable=True), + ) + op.drop_column("payments_autorecharge", "top_up_countdown") + op.drop_column("payments_autorecharge", "min_balance_in_usd") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "payments_autorecharge", + sa.Column( + "min_balance_in_usd", + sa.NUMERIC(), + server_default=sa.text("0"), + autoincrement=False, + nullable=False, + ), + ) + op.add_column( + "payments_autorecharge", + sa.Column("top_up_countdown", sa.INTEGER(), autoincrement=False, nullable=True), + ) + op.drop_column("payments_autorecharge", "monthly_limit_in_usd") + # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py b/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py index 490e23d8780..3b46e217fcc 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/payments_autorecharge.py @@ -60,37 +60,23 @@ # delete the line which will result in wallet default introduced by the api-layer. The only dissadvantage is that # the user would loose his previous settings. ), - sa.Column( - "min_balance_in_usd", - sa.Numeric(**NUMERIC_KWARGS), # type: ignore - nullable=False, - server_default=sa.text("0"), - doc="[Required] Minimum or equal balance in USD that triggers auto-recharge", - ), sa.Column( "top_up_amount_in_usd", sa.Numeric(**NUMERIC_KWARGS), # type: ignore nullable=False, - doc="[Required] Increase in USD when balance reaches min_balance_in_usd", + doc="[Required] Increase in USD when balance reaches minimum balance threshold", ), sa.Column( - "top_up_countdown", - sa.Integer(), + "monthly_limit_in_usd", + sa.Numeric(**NUMERIC_KWARGS), # type: ignore nullable=True, server_default=None, - doc="[Optional] Number of auto-recharges left." - "If it reaches zero, then auto-recharge stops." - "Used to limit the number of times that the system can auto-recharge." - "If None, then inc has no limit.", + doc="[Optional] Maximum amount in USD charged within a natural month" + "If None, indicates no limit", ), # time-stamps column_created_datetime(timezone=True), column_modified_datetime(timezone=True), - # - sa.CheckConstraint( - "(top_up_countdown >= 0) OR (top_up_countdown IS NULL)", - name="check_top_up_countdown_nonnegative", - ), ) diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_payments_autorecharge.py b/packages/postgres-database/src/simcore_postgres_database/utils_payments_autorecharge.py index 5c315738177..c8e482f26ee 100644 --- a/packages/postgres-database/src/simcore_postgres_database/utils_payments_autorecharge.py +++ b/packages/postgres-database/src/simcore_postgres_database/utils_payments_autorecharge.py @@ -26,9 +26,8 @@ def get_wallet_autorecharge(wallet_id) -> sa.sql.Select: payments_methods.c.wallet_id, payments_autorecharge.c.primary_payment_method_id, payments_autorecharge.c.enabled, - payments_autorecharge.c.min_balance_in_usd, payments_autorecharge.c.top_up_amount_in_usd, - payments_autorecharge.c.top_up_countdown, + payments_autorecharge.c.monthly_limit_in_usd, ) .select_from( payments_methods.join( @@ -52,9 +51,8 @@ def upsert_wallet_autorecharge( wallet_id, enabled, primary_payment_method_id, - min_balance_in_usd, top_up_amount_in_usd, - top_up_countdown, + monthly_limit_in_usd, ): # using this primary payment-method, create an autorecharge # NOTE: requires the entire @@ -62,9 +60,8 @@ def upsert_wallet_autorecharge( "wallet_id": wallet_id, "enabled": enabled, "primary_payment_method_id": primary_payment_method_id, - "min_balance_in_usd": min_balance_in_usd, "top_up_amount_in_usd": top_up_amount_in_usd, - "top_up_countdown": top_up_countdown, + "monthly_limit_in_usd": monthly_limit_in_usd, } insert_stmt = pg_insert(payments_autorecharge).values(**values) @@ -81,17 +78,3 @@ def update_wallet_autorecharge(wallet_id, **values) -> sa.sql.Update: .where(payments_autorecharge.c.wallet_id == wallet_id) .returning(payments_autorecharge.c.id) ) - - @staticmethod - def decrease_wallet_autorecharge_countdown(wallet_id) -> sa.sql.Update: - return ( - payments_autorecharge.update() - .where( - (payments_autorecharge.c.wallet_id == wallet_id) - & (payments_autorecharge.c.top_up_countdown is not None) - ) - .values( - top_up_countdown=payments_autorecharge.c.top_up_countdown - 1, - ) - .returning(payments_autorecharge.c.top_up_countdown) - ) diff --git a/packages/postgres-database/tests/test_utils_payments_autorecharge.py b/packages/postgres-database/tests/test_utils_payments_autorecharge.py index 4b9047e1736..fa8a67dd0e8 100644 --- a/packages/postgres-database/tests/test_utils_payments_autorecharge.py +++ b/packages/postgres-database/tests/test_utils_payments_autorecharge.py @@ -12,7 +12,6 @@ from aiopg.sa.result import RowProxy from faker import Faker from pytest_simcore.helpers.rawdata_fakers import random_payment_method, utcnow -from simcore_postgres_database import errors from simcore_postgres_database.models.payments_methods import ( InitPromptAckFlowState, payments_methods, @@ -47,9 +46,8 @@ async def _upsert_autorecharge( wallet_id, enabled, primary_payment_method_id, - min_balance_in_usd, top_up_amount_in_usd, - top_up_countdown, + monthly_limit_in_usd, ) -> RowProxy: # using this primary payment-method, create an autorecharge # NOTE: requires the entire @@ -57,9 +55,8 @@ async def _upsert_autorecharge( wallet_id=wallet_id, enabled=enabled, primary_payment_method_id=primary_payment_method_id, - min_balance_in_usd=min_balance_in_usd, top_up_amount_in_usd=top_up_amount_in_usd, - top_up_countdown=top_up_countdown, + monthly_limit_in_usd=monthly_limit_in_usd, ) row = await (await connection.execute(stmt)).first() assert row @@ -71,12 +68,6 @@ async def _update_autorecharge(connection, wallet_id, **settings) -> int | None: return await connection.scalar(stmt) -async def _decrease_countdown(connection, wallet_id) -> int | None: - stmt = AutoRechargeStmts.decrease_wallet_autorecharge_countdown(wallet_id) - # updates payments countdown - return await connection.scalar(stmt) - - PaymentMethodRow: TypeAlias = RowProxy @@ -123,9 +114,8 @@ async def test_payments_automation_workflow( wallet_id, enabled=True, primary_payment_method_id=payment_method_id, - min_balance_in_usd=10, top_up_amount_in_usd=100, - top_up_countdown=5, + monthly_limit_in_usd=None, ) auto_recharge = await _get_auto_recharge(connection, wallet_id) @@ -133,31 +123,15 @@ async def test_payments_automation_workflow( assert auto_recharge.primary_payment_method_id == payment_method_id assert auto_recharge.enabled is True - # countdown - assert await _decrease_countdown(connection, wallet_id) == 4 - assert await _decrease_countdown(connection, wallet_id) == 3 - assert await _decrease_countdown(connection, wallet_id) == 2 - assert await _decrease_countdown(connection, wallet_id) == 1 - assert await _decrease_countdown(connection, wallet_id) == 0 - - with pytest.raises(errors.CheckViolation) as err_info: - await _decrease_countdown(connection, wallet_id) - - exc = err_info.value - assert exc.pgerror - assert "check_top_up_countdown_nonnegative" in exc.pgerror - # upsert: deactivate countdown auto_recharge = await _upsert_autorecharge( connection, wallet_id, enabled=True, primary_payment_method_id=payment_method_id, - min_balance_in_usd=10, top_up_amount_in_usd=100, - top_up_countdown=None, # <---- + monthly_limit_in_usd=10000, # <---- ) - assert auto_recharge.top_up_countdown is None + assert auto_recharge.monthly_limit_in_usd == 10000 - await _update_autorecharge(connection, wallet_id, top_up_countdown=None) - assert await _decrease_countdown(connection, wallet_id) is None + await _update_autorecharge(connection, wallet_id, monthly_limit_in_usd=None) diff --git a/services/docker-compose.yml b/services/docker-compose.yml index 48c81ef14e1..a2b43963e12 100644 --- a/services/docker-compose.yml +++ b/services/docker-compose.yml @@ -420,8 +420,9 @@ services: TWILIO_COUNTRY_CODES_W_ALPHANUMERIC_SID_SUPPORT: ${TWILIO_COUNTRY_CODES_W_ALPHANUMERIC_SID_SUPPORT} WEBSERVER_PAYMENTS: ${WEBSERVER_PAYMENTS} - PAYMENTS_AUTORECHARGE_DEFAULT_MIN_BALANCE: ${PAYMENTS_AUTORECHARGE_DEFAULT_MIN_BALANCE} + PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT: ${PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT} PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT: ${PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT} + PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS: ${PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS} PAYMENTS_FAKE_COMPLETION_DELAY_SEC: ${PAYMENTS_FAKE_COMPLETION_DELAY_SEC} PAYMENTS_FAKE_COMPLETION: ${PAYMENTS_FAKE_COMPLETION} PAYMENTS_FAKE_GATEWAY_URL: ${PAYMENTS_GATEWAY_URL} diff --git a/services/invitations/openapi.json b/services/invitations/openapi.json index 32714d47661..aa110308ffe 100644 --- a/services/invitations/openapi.json +++ b/services/invitations/openapi.json @@ -173,7 +173,7 @@ "maxLength": 30, "minLength": 1, "type": "string", - "description": "Identifies who issued the invitation. E.g. an email, a service name etc" + "description": "Identifies who issued the invitation. E.g. an email, a service name etc. NOTE: it will be trimmed if exceeds maximum" }, "guest": { "title": "Guest", @@ -225,7 +225,7 @@ "maxLength": 30, "minLength": 1, "type": "string", - "description": "Identifies who issued the invitation. E.g. an email, a service name etc" + "description": "Identifies who issued the invitation. E.g. an email, a service name etc. NOTE: it will be trimmed if exceeds maximum" }, "guest": { "title": "Guest", @@ -284,7 +284,7 @@ "maxLength": 30, "minLength": 1, "type": "string", - "description": "Identifies who issued the invitation. E.g. an email, a service name etc" + "description": "Identifies who issued the invitation. E.g. an email, a service name etc. NOTE: it will be trimmed if exceeds maximum" }, "guest": { "title": "Guest", diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index eb714d9b636..2ff2af4c178 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -1854,7 +1854,8 @@ paths: schema: title: Product Name anyOf: - - minLength: 1 + - maxLength: 50 + minLength: 1 type: string - enum: - current @@ -4178,6 +4179,7 @@ paths: - required: true schema: title: Payment Id + maxLength: 50 minLength: 1 type: string name: payment_id @@ -4225,6 +4227,7 @@ paths: - required: true schema: title: Payment Method Id + maxLength: 50 minLength: 1 type: string name: payment_method_id @@ -4273,6 +4276,7 @@ paths: - required: true schema: title: Payment Method Id + maxLength: 50 minLength: 1 type: string name: payment_method_id @@ -4301,6 +4305,7 @@ paths: - required: true schema: title: Payment Method Id + maxLength: 50 minLength: 1 type: string name: payment_method_id @@ -6528,6 +6533,7 @@ components: - paymentMethodId - minBalanceInUsd - topUpAmountInUsd + - monthlyLimitInUsd type: object properties: enabled: @@ -6537,6 +6543,7 @@ components: default: false paymentMethodId: title: Paymentmethodid + maxLength: 50 minLength: 1 type: string description: Payment method in the wallet used to perform the auto-recharge @@ -6545,18 +6552,19 @@ components: title: Minbalanceinusd minimum: 0.0 type: number - description: Minimum balance in USD that triggers an auto-recharge + description: Minimum balance in USD that triggers an auto-recharge [Read + only] topUpAmountInUsd: title: Topupamountinusd minimum: 0.0 type: number description: Amount in USD payed when auto-recharge condition is satisfied - topUpCountdown: - title: Topupcountdown - exclusiveMinimum: true - type: integer - description: Maximum number of top-ups left or None to denote unlimited - minimum: 0 + monthlyLimitInUsd: + title: Monthlylimitinusd + minimum: 0.0 + type: number + description: Maximum amount in USD charged within a natural month.None indicates + no limit. GroupAccessRights: title: GroupAccessRights required: @@ -7498,6 +7506,7 @@ components: properties: idr: title: Idr + maxLength: 50 minLength: 1 type: string walletId: @@ -7553,6 +7562,7 @@ components: minimum: 0 paymentMethodId: title: Paymentmethodid + maxLength: 50 minLength: 1 type: string paymentMethodFormUrl: @@ -7575,6 +7585,7 @@ components: properties: paymentId: title: Paymentid + maxLength: 50 minLength: 1 type: string priceDollars: @@ -8589,7 +8600,6 @@ components: required: - enabled - paymentMethodId - - minBalanceInUsd - topUpAmountInUsd type: object properties: @@ -8598,21 +8608,17 @@ components: type: boolean paymentMethodId: title: Paymentmethodid + maxLength: 50 minLength: 1 type: string - minBalanceInUsd: - title: Minbalanceinusd - minimum: 0.0 - type: number topUpAmountInUsd: title: Topupamountinusd minimum: 0.0 type: number - topUpCountdown: - title: Topupcountdown - exclusiveMinimum: true - type: integer - minimum: 0 + monthlyLimitInUsd: + title: Monthlylimitinusd + minimum: 0.0 + type: number RepoApiModel: title: RepoApiModel required: @@ -9991,6 +9997,7 @@ components: properties: paymentId: title: Paymentid + maxLength: 50 minLength: 1 type: string paymentFormUrl: diff --git a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_api.py b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_api.py index bd0605cb731..346e48652a7 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_api.py +++ b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_api.py @@ -5,6 +5,7 @@ GetWalletAutoRecharge, ReplaceWalletAutoRecharge, ) +from models_library.basic_types import NonNegativeDecimal from models_library.products import ProductName from models_library.users import UserID from models_library.wallets import WalletID @@ -21,13 +22,15 @@ _logger = logging.getLogger(__name__) -def _from_db_to_api_model(db_model: PaymentsAutorechargeDB) -> GetWalletAutoRecharge: +def _from_db_to_api_model( + db_model: PaymentsAutorechargeDB, min_balance_in_credits: NonNegativeDecimal +) -> GetWalletAutoRecharge: return GetWalletAutoRecharge( enabled=db_model.enabled, payment_method_id=db_model.primary_payment_method_id, - min_balance_in_usd=db_model.min_balance_in_usd, + min_balance_in_credits=min_balance_in_credits, top_up_amount_in_usd=db_model.top_up_amount_in_usd, - top_up_countdown=db_model.top_up_countdown, + monthly_limit_in_usd=db_model.monthly_limit_in_usd, ) @@ -38,9 +41,8 @@ def _from_api_to_db_model( wallet_id=wallet_id, enabled=api_model.enabled, primary_payment_method_id=api_model.payment_method_id, - min_balance_in_usd=api_model.min_balance_in_usd, top_up_amount_in_usd=api_model.top_up_amount_in_usd, - top_up_countdown=api_model.top_up_countdown, + monthly_limit_in_usd=api_model.monthly_limit_in_usd, ) @@ -61,12 +63,11 @@ async def get_wallet_payment_autorecharge( await raise_for_wallet_payments_permissions( app, user_id=user_id, wallet_id=wallet_id, product_name=product_name ) - + settings = get_plugin_settings(app) got: PaymentsAutorechargeDB | None = await get_wallet_autorecharge( app, wallet_id=wallet_id ) if not got: - settings = get_plugin_settings(app) payment_method_id = None wallet_payment_methods = await list_successful_payment_methods( app, @@ -79,12 +80,15 @@ async def get_wallet_payment_autorecharge( return GetWalletAutoRecharge( enabled=False, payment_method_id=payment_method_id, - min_balance_in_usd=settings.PAYMENTS_AUTORECHARGE_DEFAULT_MIN_BALANCE, + min_balance_in_credits=settings.PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS, top_up_amount_in_usd=settings.PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT, - top_up_countdown=None, + monthly_limit_in_usd=settings.PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT, ) - return _from_db_to_api_model(got) + return _from_db_to_api_model( + got, + min_balance_in_credits=settings.PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS, + ) async def replace_wallet_payment_autorecharge( @@ -99,6 +103,7 @@ async def replace_wallet_payment_autorecharge( app, user_id=user_id, wallet_id=wallet_id, product_name=product_name ) + settings = get_plugin_settings(app) got: PaymentsAutorechargeDB = await replace_wallet_autorecharge( app, user_id=user_id, @@ -106,4 +111,7 @@ async def replace_wallet_payment_autorecharge( new=_from_api_to_db_model(wallet_id, new), ) - return _from_db_to_api_model(got) + return _from_db_to_api_model( + got, + min_balance_in_credits=settings.PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS, + ) diff --git a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py index 33ab1698bb2..8aec3e45359 100644 --- a/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py +++ b/services/web/server/src/simcore_service_webserver/payments/_autorecharge_db.py @@ -22,9 +22,8 @@ class PaymentsAutorechargeDB(BaseModel): wallet_id: WalletID enabled: bool primary_payment_method_id: PaymentMethodID - min_balance_in_usd: NonNegativeDecimal top_up_amount_in_usd: NonNegativeDecimal - top_up_countdown: PositiveInt | None + monthly_limit_in_usd: NonNegativeDecimal | None class Config: orm_mode = True @@ -70,9 +69,8 @@ async def replace_wallet_autorecharge( wallet_id=wallet_id, enabled=new.enabled, primary_payment_method_id=new.primary_payment_method_id, - min_balance_in_usd=new.min_balance_in_usd, top_up_amount_in_usd=new.top_up_amount_in_usd, - top_up_countdown=new.top_up_countdown, + monthly_limit_in_usd=new.monthly_limit_in_usd, ) result = await conn.execute(stmt) row = await result.first() diff --git a/services/web/server/src/simcore_service_webserver/payments/settings.py b/services/web/server/src/simcore_service_webserver/payments/settings.py index 9d3de61d0e1..ea5d1db43ed 100644 --- a/services/web/server/src/simcore_service_webserver/payments/settings.py +++ b/services/web/server/src/simcore_service_webserver/payments/settings.py @@ -46,14 +46,20 @@ class PaymentsSettings(BaseCustomSettings, MixinServiceSettings): description="FAKE Base url to the payment gateway", ) - PAYMENTS_AUTORECHARGE_DEFAULT_MIN_BALANCE: NonNegativeDecimal = Field( - default=20.0, - description="Default value on the minimum balance to top-up for auto-recharge", + PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS: NonNegativeDecimal = Field( + default=100, + description="Minimum balance in credits to top-up for auto-recharge", + # NOTE: Using credits (instead of USD) simplify RUT monitoring which is reponsible to trigger auto-recharge ) PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT: NonNegativeDecimal = Field( - default=100.0, - description="Default value on the amount to top-up for auto-recharge", + default=100, + description="Default value in USD on the amount to top-up for auto-recharge (`top_up_amount_in_usd`)", + ) + + PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT: NonNegativeDecimal | None = Field( + default=10000, + description="Default value in USD for the montly limit for auto-recharge (`monthly_limit_in_usd`)", ) @cached_property diff --git a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py index 61b380efafc..a9841b276c6 100644 --- a/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py +++ b/services/web/server/tests/unit/with_dbs/03/wallets/payments/test_payments_methods.py @@ -22,9 +22,9 @@ from simcore_service_webserver.payments._methods_api import ( _ack_creation_of_wallet_payment_method, ) +from simcore_service_webserver.payments.settings import PaymentsSettings from simcore_service_webserver.payments.settings import ( - PaymentsSettings, - get_plugin_settings, + get_plugin_settings as get_payments_plugin_settings, ) @@ -38,7 +38,7 @@ async def test_payment_method_worfklow( ): # preamble assert client.app - settings: PaymentsSettings = get_plugin_settings(client.app) + settings: PaymentsSettings = get_payments_plugin_settings(client.app) assert settings.PAYMENTS_FAKE_COMPLETION is False @@ -164,6 +164,8 @@ async def test_wallet_autorecharge( client: TestClient, logged_user_wallet: WalletGet, ): + assert client.app + settings = get_payments_plugin_settings(client.app) wallet = logged_user_wallet # get default @@ -172,8 +174,19 @@ async def test_wallet_autorecharge( data, _ = await assert_status(response, web.HTTPOk) default_auto_recharge = GetWalletAutoRecharge(**data) assert default_auto_recharge.enabled is False - assert default_auto_recharge.top_up_countdown is None assert default_auto_recharge.payment_method_id is None + assert ( + default_auto_recharge.min_balance_in_credits + == settings.PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS + ) + assert ( + default_auto_recharge.top_up_amount_in_usd + == settings.PAYMENTS_AUTORECHARGE_DEFAULT_TOP_UP_AMOUNT + ) + assert ( + default_auto_recharge.monthly_limit_in_usd + == settings.PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT + ) # A wallet with a payment method older_payment_method_id = await _add_payment_method( @@ -186,7 +199,10 @@ async def test_wallet_autorecharge( data, _ = await assert_status(response, web.HTTPOk) default_auto_recharge = GetWalletAutoRecharge(**data) assert default_auto_recharge.enabled is False - assert default_auto_recharge.top_up_countdown is None + assert ( + default_auto_recharge.monthly_limit_in_usd + == settings.PAYMENTS_AUTORECHARGE_DEFAULT_MONTHLY_LIMIT + ) assert default_auto_recharge.payment_method_id == payment_method_id # Activate auto-rechange @@ -194,9 +210,8 @@ async def test_wallet_autorecharge( f"/v0/wallets/{wallet.wallet_id}/auto-recharge", json={ "paymentMethodId": payment_method_id, - "minBalanceInUsd": 0.0, - "topUpAmountInUsd": 100.0, # $ - "topUpCountdown": 3, + "topUpAmountInUsd": 123.45, # $ + "monthlyLimitInUsd": 6543.21, # $ "enabled": True, }, ) @@ -204,9 +219,9 @@ async def test_wallet_autorecharge( updated_auto_recharge = GetWalletAutoRecharge.parse_obj(data) assert updated_auto_recharge == GetWalletAutoRecharge( payment_method_id=payment_method_id, - min_balance_in_usd=0.0, - top_up_amount_in_usd=100.0, # $ - top_up_countdown=3, + min_balance_in_credits=settings.PAYMENTS_AUTORECHARGE_MIN_BALANCE_IN_CREDITS, + top_up_amount_in_usd=123.45, # $ + monthly_limit_in_usd=6543.21, # $ enabled=True, ) @@ -236,6 +251,8 @@ async def test_delete_primary_payment_method_in_autorecharge( client: TestClient, logged_user_wallet: WalletGet, ): + assert client.app + wallet = logged_user_wallet payment_method_id = await _add_payment_method(client, wallet_id=wallet.wallet_id) @@ -244,9 +261,8 @@ async def test_delete_primary_payment_method_in_autorecharge( f"/v0/wallets/{wallet.wallet_id}/auto-recharge", json={ "paymentMethodId": payment_method_id, - "minBalanceInUsd": 0.0, "topUpAmountInUsd": 100.0, # $ - "topUpCountdown": 3, + "monthlyLimitInUsd": 123, "enabled": True, }, ) @@ -254,6 +270,7 @@ async def test_delete_primary_payment_method_in_autorecharge( auto_recharge = GetWalletAutoRecharge.parse_obj(data) assert auto_recharge.enabled is True assert auto_recharge.payment_method_id == payment_method_id + assert auto_recharge.monthly_limit_in_usd == 123 # delete payment-method response = await client.delete(