Skip to content

Commit 6f506e5

Browse files
🎨 2. Part: Invoice expiration fix (OPS ⚠️) (ITISFoundation#5537)
1 parent ccca61a commit 6f506e5

File tree

21 files changed

+651
-8
lines changed

21 files changed

+651
-8
lines changed

.env-devel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ PAYMENTS_HOST=payments
9999
PAYMENTS_LOGLEVEL=INFO
100100
PAYMENTS_PASSWORD=adminadmin
101101
PAYMENTS_PORT=8000
102+
PAYMENTS_STRIPE_URL=https://api.stripe.com
103+
PAYMENTS_STRIPE_API_SECRET='REPLACE_ME_with_api_secret'
102104
PAYMENTS_SWAGGER_API_DOC_ENABLED=1
103105
PAYMENTS_USERNAME=admin
104106

api/specs/web-server/_wallets.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,19 @@ async def list_all_payments(params: Annotated[PageQueryParameters, Depends()]):
105105
"""Lists all user payments to his/her wallets (only the ones he/she created)"""
106106

107107

108+
@router.get(
109+
"/wallets/{wallet_id}/payments/{payment_id}/invoice-link",
110+
status_code=status.HTTP_302_FOUND,
111+
responses={
112+
status.HTTP_302_FOUND: {
113+
"description": "redirection to invoice download link",
114+
}
115+
},
116+
)
117+
async def get_payment_invoice_link(wallet_id: WalletID, payment_id: PaymentID):
118+
...
119+
120+
108121
@router.post(
109122
"/wallets/{wallet_id}/payments/{payment_id}:cancel",
110123
response_description="Successfully cancelled",

services/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ services:
369369
PAYMENTS_GATEWAY_URL: ${PAYMENTS_GATEWAY_URL}
370370
PAYMENTS_LOGLEVEL: ${PAYMENTS_LOGLEVEL}
371371
PAYMENTS_PASSWORD: ${PAYMENTS_PASSWORD}
372+
PAYMENTS_STRIPE_URL: ${PAYMENTS_STRIPE_URL}
373+
PAYMENTS_STRIPE_API_SECRET: ${PAYMENTS_STRIPE_API_SECRET}
372374
PAYMENTS_SWAGGER_API_DOC_ENABLED: ${PAYMENTS_SWAGGER_API_DOC_ENABLED}
373375
PAYMENTS_USERNAME: ${PAYMENTS_USERNAME}
374376
POSTGRES_DB: ${POSTGRES_DB}

services/payments/src/simcore_service_payments/api/rpc/_payments.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
from models_library.products import ProductName, StripePriceID, StripeTaxRateID
1616
from models_library.users import UserID
1717
from models_library.wallets import WalletID
18-
from pydantic import EmailStr
18+
from pydantic import EmailStr, HttpUrl
1919
from servicelib.logging_utils import get_log_record_extra, log_context
2020
from servicelib.rabbitmq import RPCRouter
2121

2222
from ...db.payments_transactions_repo import PaymentsTransactionsRepo
2323
from ...services import payments
2424
from ...services.payments_gateway import PaymentsGatewayApi
25+
from ...services.stripe import StripeApi
2526

2627
_logger = logging.getLogger(__name__)
2728

@@ -112,3 +113,20 @@ async def get_payments_page(
112113
limit=limit,
113114
offset=offset,
114115
)
116+
117+
118+
@router.expose(reraise_if_error_type=(PaymentsError, PaymentServiceUnavailableError))
119+
async def get_payment_invoice_url(
120+
app: FastAPI,
121+
*,
122+
user_id: UserID,
123+
wallet_id: WalletID,
124+
payment_id: PaymentID,
125+
) -> HttpUrl:
126+
return await payments.get_payment_invoice_url(
127+
repo=PaymentsTransactionsRepo(db_engine=app.state.engine),
128+
stripe_api=StripeApi.get_from_app_state(app),
129+
user_id=user_id,
130+
wallet_id=wallet_id,
131+
payment_id=payment_id,
132+
)

services/payments/src/simcore_service_payments/cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ def echo_dotenv(
8787
),
8888
),
8989
),
90+
PAYMENTS_STRIPE_URL=os.environ.get(
91+
"PAYMENTS_STRIPE_URL", "https://api.stripe.com"
92+
),
93+
PAYMENTS_STRIPE_API_SECRET=os.environ.get(
94+
"PAYMENTS_STRIPE_API_SECRET", "replace-with-api-secret"
95+
),
9096
)
9197

9298
print_as_envfile(

services/payments/src/simcore_service_payments/core/application.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
from servicelib.fastapi.prometheus_instrumentation import (
44
setup_prometheus_instrumentation,
55
)
6-
from simcore_service_payments.services.notifier import setup_notifier
7-
from simcore_service_payments.services.socketio import setup_socketio
86

97
from .._meta import (
108
API_VERSION,
@@ -17,10 +15,13 @@
1715
from ..api.rest.routes import setup_rest_api
1816
from ..api.rpc.routes import setup_rpc_api_routes
1917
from ..services.auto_recharge_listener import setup_auto_recharge_listener
18+
from ..services.notifier import setup_notifier
2019
from ..services.payments_gateway import setup_payments_gateway
2120
from ..services.postgres import setup_postgres
2221
from ..services.rabbitmq import setup_rabbitmq
2322
from ..services.resource_usage_tracker import setup_resource_usage_tracker
23+
from ..services.socketio import setup_socketio
24+
from ..services.stripe import setup_stripe
2425
from .settings import ApplicationSettings
2526

2627

@@ -57,6 +58,9 @@ def create_app(settings: ApplicationSettings | None = None) -> FastAPI:
5758
# APIs w/ RUT
5859
setup_resource_usage_tracker(app)
5960

61+
# APIs w/ Stripe
62+
setup_stripe(app)
63+
6064
# Listening to Rabbitmq
6165
setup_auto_recharge_listener(app)
6266
setup_socketio(app)

services/payments/src/simcore_service_payments/core/errors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,12 @@ class PaymentsGatewayError(_BaseAppError):
1919

2020
class PaymentsGatewayNotReadyError(PaymentsGatewayError):
2121
msg_template = "Payments-Gateway is unresponsive: {checks}"
22+
23+
24+
#
25+
# stripe errors
26+
#
27+
28+
29+
class StripeRuntimeError(_BaseAppError):
30+
msg_template = "Stripe unexpected error"

services/payments/src/simcore_service_payments/core/settings.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ class ApplicationSettings(_BaseApplicationSettings):
110110
auto_default_from_env=True, description="settings for postgres service"
111111
)
112112

113+
PAYMENTS_STRIPE_URL: HttpUrl = Field(
114+
..., description="Base url to the payment Stripe"
115+
)
116+
PAYMENTS_STRIPE_API_SECRET: SecretStr = Field(
117+
..., description="Credentials for Stripe api"
118+
)
119+
113120
PAYMENTS_SWAGGER_API_DOC_ENABLED: bool = Field(
114121
default=True, description="If true, it displays swagger doc at /doc"
115122
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pydantic import BaseModel, HttpUrl
2+
3+
4+
class InvoiceData(BaseModel):
5+
hosted_invoice_url: HttpUrl

services/payments/src/simcore_service_payments/services/payments.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from models_library.products import ProductName, StripePriceID, StripeTaxRateID
2626
from models_library.users import UserID
2727
from models_library.wallets import WalletID
28-
from pydantic import EmailStr, PositiveInt
28+
from pydantic import EmailStr, HttpUrl, PositiveInt
2929
from servicelib.logging_utils import log_context
3030
from simcore_postgres_database.models.payments_transactions import (
3131
PaymentTransactionState,
@@ -46,7 +46,9 @@
4646
StripeTaxExempt,
4747
)
4848
from ..models.schemas.acknowledgements import AckPayment, AckPaymentWithPaymentMethod
49+
from ..models.stripe import InvoiceData
4950
from ..services.resource_usage_tracker import ResourceUsageTrackerApi
51+
from ..services.stripe import StripeApi
5052
from .notifier import NotifierService
5153
from .notifier_ws import WebSocketProvider
5254
from .payments_gateway import PaymentsGatewayApi
@@ -313,3 +315,24 @@ async def get_payments_page(
313315
)
314316

315317
return total_number_of_items, [to_payments_api_model(t) for t in page]
318+
319+
320+
async def get_payment_invoice_url(
321+
repo: PaymentsTransactionsRepo,
322+
stripe_api: StripeApi,
323+
*,
324+
user_id: UserID,
325+
wallet_id: WalletID,
326+
payment_id: PaymentID,
327+
) -> HttpUrl:
328+
"""Get invoice data from Stripe. As invoice url expires after some time, Stripe always generates a new
329+
invoice url with 10 day validity."""
330+
331+
payment: PaymentsTransactionsDB | None = await repo.get_payment_transaction(
332+
payment_id=payment_id, user_id=user_id, wallet_id=wallet_id
333+
)
334+
if payment is None or payment.stripe_invoice_id is None:
335+
raise PaymentNotFoundError(payment_id=payment_id)
336+
invoice_data: InvoiceData = await stripe_api.get_invoice(payment.stripe_invoice_id)
337+
338+
return invoice_data.hosted_invoice_url

0 commit comments

Comments
 (0)