Client-side `/credits` PATCH
This commit is contained in:
parent
2caf498b2e
commit
deddcddb62
|
@ -80,12 +80,13 @@ class UserCreditBase(ABC):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def fulfill_checkout(self, session_id):
|
||||
async def fulfill_checkout(self, *, session_id: str | None = None, user_id: str | None = None):
|
||||
"""
|
||||
Fulfill the Stripe checkout session.
|
||||
|
||||
Args:
|
||||
session_id (str): The checkout session ID.
|
||||
session_id (str | None): The checkout session ID. Will try to fulfill most recent if None.
|
||||
user_id (str | None): The user ID must be provided if session_id is None.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -296,10 +297,21 @@ class UserCredit(UserCreditBase):
|
|||
return checkout_session.url or ""
|
||||
|
||||
# https://docs.stripe.com/checkout/fulfillment
|
||||
async def fulfill_checkout(self, session_id):
|
||||
async def fulfill_checkout(self, *, session_id: str | None = None, user_id: str | None = None):
|
||||
if (not session_id and not user_id) or (session_id and user_id):
|
||||
raise ValueError("Either session_id or user_id must be provided")
|
||||
|
||||
# Retrieve CreditTransaction
|
||||
credit_transaction = await CreditTransaction.prisma().find_first_or_raise(
|
||||
where={"transactionKey": session_id}
|
||||
where={
|
||||
"OR": [
|
||||
{"transactionKey": session_id} if session_id is not None else {"transactionKey": ""},
|
||||
{"userId": user_id} if user_id is not None else {"userId": ""}
|
||||
]
|
||||
},
|
||||
order={
|
||||
"createdAt": "desc"
|
||||
}
|
||||
)
|
||||
|
||||
# This can be called multiple times for one id, so ignore if already fulfilled
|
||||
|
@ -307,7 +319,7 @@ class UserCredit(UserCreditBase):
|
|||
return
|
||||
|
||||
# Retrieve the Checkout Session from the API
|
||||
checkout_session = stripe.checkout.Session.retrieve(session_id)
|
||||
checkout_session = stripe.checkout.Session.retrieve(credit_transaction.transactionKey)
|
||||
|
||||
# Check the Checkout Session's payment_status property
|
||||
# to determine if fulfillment should be peformed
|
||||
|
@ -316,7 +328,7 @@ class UserCredit(UserCreditBase):
|
|||
await CreditTransaction.prisma().update(
|
||||
where={
|
||||
"creditTransactionIdentifier": {
|
||||
"transactionKey": session_id,
|
||||
"transactionKey": credit_transaction.transactionKey,
|
||||
"userId": credit_transaction.userId,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -149,6 +149,14 @@ async def request_top_up(
|
|||
return {"checkout_url": checkout_url}
|
||||
|
||||
|
||||
@v1_router.patch(
|
||||
path="/credits", tags=["credits"], dependencies=[Depends(auth_middleware)]
|
||||
)
|
||||
async def fulfill_checkout(user_id: Annotated[str, Depends(get_user_id)]):
|
||||
await _user_credit_model.fulfill_checkout(user_id=user_id)
|
||||
return Response(status_code=200)
|
||||
|
||||
|
||||
@v1_router.post(path="/credits/stripe_webhook", tags=["credits"])
|
||||
async def stripe_webhook(request: Request):
|
||||
# Get the raw request body
|
||||
|
@ -171,7 +179,7 @@ async def stripe_webhook(request: Request):
|
|||
event["type"] == "checkout.session.completed"
|
||||
or event["type"] == "checkout.session.async_payment_succeeded"
|
||||
):
|
||||
await _user_credit_model.fulfill_checkout(event["data"]["object"]["id"])
|
||||
await _user_credit_model.fulfill_checkout(session_id=event["data"]["object"]["id"])
|
||||
|
||||
return Response(status_code=200)
|
||||
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
"use client";
|
||||
import { Button } from "@/components/agptui/Button";
|
||||
import useCredits from "@/hooks/useCredits";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function CreditsPage() {
|
||||
const { credits, requestTopUp } = useCredits();
|
||||
const [amount, setAmount] = useState(5);
|
||||
const [patched, setPatched] = useState(false);
|
||||
const searchParams = useSearchParams();
|
||||
const topupStatus = searchParams.get("topup");
|
||||
const api = useBackendAPI();
|
||||
|
||||
useEffect(() => {
|
||||
if (!patched && topupStatus === "success") {
|
||||
api.fulfillCheckout();
|
||||
setPatched(true);
|
||||
}
|
||||
}, [topupStatus]);
|
||||
|
||||
return (
|
||||
<div className="w-full min-w-[800px] px-4 sm:px-8">
|
||||
|
|
|
@ -89,6 +89,10 @@ export default class BackendAPI {
|
|||
return this._request("POST", "/credits", { amount });
|
||||
}
|
||||
|
||||
fulfillCheckout(): Promise<void> {
|
||||
return this._request("PATCH", "/credits");
|
||||
}
|
||||
|
||||
getBlocks(): Promise<Block[]> {
|
||||
return this._get("/blocks");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue