Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions app/core/mypayment/cruds_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,11 @@ async def get_requests_by_wallet_id(
result = await db.execute(
select(models_mypayment.Request).where(
models_mypayment.Request.wallet_id == wallet_id,
models_mypayment.Request.status == RequestStatus.PROPOSED
and_(
models_mypayment.Request.status == RequestStatus.PROPOSED,
models_mypayment.Request.creation
> datetime.now(tz=UTC) - timedelta(minutes=REQUEST_EXPIRATION),
)
if not include_used
else and_(True),
),
Expand All @@ -1042,7 +1046,7 @@ async def get_requests_by_wallet_id(
wallet_id=request.wallet_id,
status=request.status,
creation=request.creation,
end_date=request.end_date,
expiration_date=request.expiration_date,
total=request.total,
store_note=request.store_note,
store_id=request.store_id,
Expand Down Expand Up @@ -1075,7 +1079,7 @@ async def get_request_by_id(
wallet_id=request.wallet_id,
status=request.status,
creation=request.creation,
end_date=request.end_date,
expiration_date=request.expiration_date,
total=request.total,
store_note=request.store_note,
store_id=request.store_id,
Expand Down Expand Up @@ -1103,7 +1107,7 @@ async def get_request_by_store_id(
wallet_id=request.wallet_id,
status=request.status,
creation=request.creation,
end_date=request.end_date,
expiration_date=request.expiration_date,
total=request.total,
store_note=request.store_note,
store_id=request.store_id,
Expand Down Expand Up @@ -1135,20 +1139,6 @@ async def create_request(
db.add(request_db)


async def mark_expired_requests_as_expired(
db: AsyncSession,
) -> None:
await db.execute(
update(models_mypayment.Request)
.where(
models_mypayment.Request.status == RequestStatus.PROPOSED,
models_mypayment.Request.creation
<= datetime.now(tz=UTC) - timedelta(minutes=REQUEST_EXPIRATION),
)
.values(status=RequestStatus.EXPIRED),
)


async def update_request(
request_id: UUID,
request_update: schemas_mypayment.RequestEdit,
Expand Down
26 changes: 10 additions & 16 deletions app/core/mypayment/endpoints_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
MYPAYMENT_STRUCTURE_S3_SUBFOLDER,
MYPAYMENT_USERS_S3_SUBFOLDER,
QRCODE_EXPIRATION,
REQUEST_EXPIRATION,
RETENTION_DURATION,
HistoryDirection,
HistoryType,
Expand Down Expand Up @@ -2796,10 +2795,6 @@ async def accept_request(

**The user must be authenticated to use this endpoint**
"""
await cruds_mypayment.mark_expired_requests_as_expired(
db=db,
)
await db.flush()
if request_id != request_validation.id:
raise HTTPException(
status_code=400,
Expand All @@ -2819,6 +2814,16 @@ async def accept_request(
status_code=400,
detail="Request total in the body do not match the request total in the database",
)
if request.status != RequestStatus.PROPOSED:
raise HTTPException(
status_code=400,
detail="Only pending requests can be confirmed",
)
if request.expiration_date < datetime.now(UTC):
raise HTTPException(
status_code=400,
detail="Request is expired",
)

user_payment = await cruds_mypayment.get_user_payment(
user_id=user.id,
Expand Down Expand Up @@ -2851,17 +2856,6 @@ async def accept_request(
detail="Wallet device is not associated with the user wallet",
)

if request.status != RequestStatus.PROPOSED:
raise HTTPException(
status_code=400,
detail="Only pending requests can be confirmed",
)
if request.creation < datetime.now(UTC) - timedelta(minutes=REQUEST_EXPIRATION):
raise HTTPException(
status_code=400,
detail="Request is expired",
)

if not verify_signature(
public_key_bytes=debited_wallet_device.ed25519_public_key,
signature=request_validation.signature,
Expand Down
2 changes: 1 addition & 1 deletion app/core/mypayment/models_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class Request(Base):
)

@property
def end_date(self) -> datetime:
def expiration_date(self) -> datetime:
return self.creation + timedelta(minutes=REQUEST_EXPIRATION)


Expand Down
2 changes: 1 addition & 1 deletion app/core/mypayment/schemas_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ class Request(BaseModel):
id: UUID
wallet_id: UUID
creation: datetime
end_date: datetime
expiration_date: datetime
total: int # Stored in cents
store_id: UUID
name: str
Expand Down
1 change: 0 additions & 1 deletion app/core/mypayment/types_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ class RequestStatus(StrEnum):
PROPOSED = "proposed"
ACCEPTED = "accepted"
REFUSED = "refused"
EXPIRED = "expired"


class TransferOrigin(StrEnum):
Expand Down
5 changes: 3 additions & 2 deletions app/core/mypayment/utils_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,14 @@ async def request_transaction(
if not payment_user:
raise PaymentUserNotFoundError(user.id)
start_time = datetime.now(UTC)
expiration_time = start_time + timedelta(minutes=REQUEST_EXPIRATION)
await cruds_mypayment.create_request(
db=db,
request=schemas_mypayment.Request(
id=uuid4(),
wallet_id=payment_user.wallet_id,
creation=start_time,
end_date=start_time + timedelta(minutes=REQUEST_EXPIRATION),
expiration_date=expiration_time,
total=request_info.total,
store_id=request_info.store_id,
name=request_info.request_name,
Expand All @@ -198,7 +199,7 @@ async def request_transaction(
message=message,
)
return schemas_mypayment.PaymentRequestInfo(
end_date=start_time + timedelta(minutes=REQUEST_EXPIRATION),
end_date=expiration_time,
checkout_url=None,
)

Expand Down
57 changes: 57 additions & 0 deletions migrations/versions/70-better_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""empty message

Create Date: 2026-04-20 00:51:44.716180
"""

from collections.abc import Sequence
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pytest_alembic import MigrationContext

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "9bb79b2466f9"
down_revision: str | None = "c4d2aa4e6f1b"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
conn = op.get_bind()
conn.execute(
sa.text(
"""
DELETE FROM pg_enum WHERE enumtypid = (
SELECT oid FROM pg_type WHERE typname = 'requeststatus'
) AND enumlabel = 'EXPIRED'
""",
),
)


def downgrade() -> None:
conn = op.get_bind()
conn.execute(
sa.text(
"""
ALTER TYPE requeststatus ADD VALUE IF NOT EXISTS 'EXPIRED'
""",
),
)


def pre_test_upgrade(
alembic_runner: "MigrationContext",
alembic_connection: sa.Connection,
) -> None:
pass


def test_upgrade(
alembic_runner: "MigrationContext",
alembic_connection: sa.Connection,
) -> None:
pass
4 changes: 2 additions & 2 deletions tests/core/test_mypayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ async def init_objects() -> None:
total=1000,
name="Expired Request",
store_note="Expired Request Note",
status=RequestStatus.EXPIRED,
status=RequestStatus.PROPOSED,
module=TEST_MODULE_ROOT,
object_id=uuid4(),
transaction_id=None,
Expand Down Expand Up @@ -3970,7 +3970,7 @@ async def test_accept_expired_request(
json=validation.model_dump(mode="json"),
)
assert response.status_code == 400
assert response.json()["detail"] == "Only pending requests can be confirmed"
assert response.json()["detail"] == "Request is expired"

mocked_callback.assert_not_called()

Expand Down
Loading