Skip to content

Commit f995cba

Browse files
committed
feat(payment): enhance top-up process with validation and update URLs in notifications
1 parent 7eb3f95 commit f995cba

10 files changed

Lines changed: 57 additions & 29 deletions

File tree

backend/src/main/java/com/namvu/realtimeauctionsystem/common/constant/NotificationConstant.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ private RedirectUrlsConstant() {
181181
}
182182

183183
public static final String HOME_URL = "/";
184-
public static final String WALLET_URL = "/account/wallet";
184+
public static final String WALLET_URL = "/account/wallet?tab=topup";
185185
public static final String AUCTION_DETAIL_URL = "/auction/%d";
186186
public static final String SELLER_AUCTION_DETAIL_URL = "/seller/auctions/%d";
187187
public static final String SELLER_DASHBOARD_URL = "/seller";
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package com.namvu.realtimeauctionsystem.modules.payment.dto;
22

3+
import jakarta.validation.constraints.Max;
34
import jakarta.validation.constraints.Min;
45
import jakarta.validation.constraints.NotNull;
56
import lombok.Data;
67

78
@Data
89
public class CreateTopUpOrderRequest {
910
@NotNull
10-
@Min(10000)
11+
@Min(value = 10000, message = "Minimum top-up amount is 10,000 VND")
12+
@Max(value = 500_000_000, message = "Maximum top-up amount is 500,000,000 VND")
1113
private Long amount;
1214
}

backend/src/main/java/com/namvu/realtimeauctionsystem/modules/payment/service/impl/PaymentServiceImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ public CheckoutFormResponse createTopUpOrder(Long userId, CreateTopUpOrderReques
9797
formFields.put("order_invoice_number", invoiceNumber);
9898
formFields.put("order_description", "Top up to wallet " + email);
9999
formFields.put("customer_id", String.valueOf(userId));
100-
formFields.put("success_url", frontendUrl + "/account/wallet?topup=success");
101-
formFields.put("error_url", frontendUrl + "/account/wallet?topup=error");
102-
formFields.put("cancel_url", frontendUrl + "/account/wallet?topup=cancelled");
100+
formFields.put("success_url", frontendUrl + "/account/wallet?tab=topup&topup=success");
101+
formFields.put("error_url", frontendUrl + "/account/wallet?tab=topup&topup=error");
102+
formFields.put("cancel_url", frontendUrl + "/account/wallet?tab=topup&topup=cancelled");
103103

104104
String signature = buildSignature(formFields);
105105

backend/src/main/resources/templates/mail/auction-approval.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ <h1 class="title">Phiên đấu giá của bạn đã được phê duyệt!
7272
</div>
7373
</div>
7474
<div class="cta-container">
75-
<a href="http://localhost:5173/seller/auctions" class="cta-btn">Xem phiên đấu giá</a>
75+
<a href="https://www.auctionpro.online/seller/auctions" class="cta-btn">Xem phiên đấu giá</a>
7676
</div>
7777
<p class="message" style="margin-bottom: 0;">
7878
Cảm ơn bạn đã sử dụng AuctionPro!<br><strong>Đội ngũ AuctionPro</strong>

backend/src/main/resources/templates/mail/auction-rejection.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ <h1 class="title">Phiên đấu giá bị từ chối kiểm duyệt</h1>
7979
Bạn có thể chỉnh sửa nội dung và gửi lại phiên đấu giá mới sau khi đã khắc phục vấn đề được nêu ở trên.
8080
</p>
8181
<div class="cta-container">
82-
<a href="http://localhost:5173/seller/auctions" class="cta-btn">Quay lại trang quản lý</a>
82+
<a href="https://www.auctionpro.online/seller/auctions" class="cta-btn">Quay lại trang quản lý</a>
8383
</div>
8484
<p class="message" style="margin-bottom: 0;">
8585
Nếu bạn có thắc mắc, vui lòng liên hệ bộ phận hỗ trợ.<br><strong>Đội ngũ AuctionPro</strong>

backend/src/main/resources/templates/mail/auction-winner.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ <h3 class="summary-title">Winning Summary</h3>
255255
<p class="message">Please complete your payment process within 24 hours to ensure priority shipping and finalize the transaction.</p>
256256

257257
<div class="cta-container">
258-
<a th:href="'http://localhost:5173/auction/' + ${auctionId} + (${token != null} ? '?token=' + ${token} : '')"
258+
<a th:href="'https://www.auctionpro.online/auction/' + ${auctionId} + (${token != null} ? '?token=' + ${token} : '')"
259259
class="cta-btn">
260260
Claim Your Item Now
261261
</a>

backend/src/main/resources/templates/mail/seller-approval.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ <h4>Expert Support</h4>
212212
</div>
213213

214214
<div class="cta-container">
215-
<a href="http://localhost:5173/seller" class="cta-btn">Go to Seller Dashboard</a>
215+
<a href="https://www.auctionpro.online/seller" class="cta-btn">Go to Seller Dashboard</a>
216216
</div>
217217

218218
<p class="message" style="margin-bottom: 0;">We're excited to see what you'll bring to the block!<br><strong>The

backend/src/main/resources/templates/mail/welcome-email.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ <h4>Secure Transactions</h4>
198198
</div>
199199

200200
<div class="cta-container">
201-
<a href="http://localhost:5173" class="cta-btn">Explore Live Auctions</a>
201+
<a href="https://www.auctionpro.online" class="cta-btn">Explore Live Auctions</a>
202202
</div>
203203

204204
<p class="message" style="margin-bottom: 0;">Happy Bidding!<br><strong>The AuctionPro Team</strong></p>

frontend/src/components/layout/Header.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,15 @@ export const Header = () => {
8383
const [loading, setLoading] = useState(false);
8484
const [topUpOpen, setTopUpOpen] = useState(false);
8585
const [topUpAmount, setTopUpAmount] = useState<number | null>(null);
86+
const [isRedirecting, setIsRedirecting] = useState(false);
8687

8788
const checkoutMutation = useMutation({
8889
mutationFn: (amt: number) => paymentApi.createTopUpOrder(amt),
89-
onSuccess: (data) => submitSePayForm(data),
90+
onSuccess: (data) => {
91+
setIsRedirecting(true);
92+
setTopUpOpen(false);
93+
submitSePayForm(data);
94+
},
9095
onError: (err: Error) => message.error(err.message),
9196
});
9297

@@ -95,6 +100,10 @@ export const Header = () => {
95100
message.error("Minimum top-up amount is 10,000 VND");
96101
return;
97102
}
103+
if (topUpAmount > 500_000_000) {
104+
message.error("Maximum top-up amount is 500,000,000 VND");
105+
return;
106+
}
98107
checkoutMutation.mutate(topUpAmount);
99108
};
100109
const [searchValue, setSearchValue] = useState("");
@@ -572,15 +581,18 @@ export const Header = () => {
572581
{/* Actions */}
573582
<div className="flex gap-2">
574583
<button
575-
onClick={() => setTopUpOpen(true)}
576-
className="flex-1 py-1.5 rounded-lg text-[12px] font-semibold font-[inherit] cursor-pointer transition-colors"
584+
onClick={() => !isRedirecting && setTopUpOpen(true)}
585+
disabled={isRedirecting}
586+
className="flex-1 py-1.5 rounded-lg text-[12px] font-semibold font-[inherit] transition-colors"
577587
style={{
578588
background: "rgba(254,212,105,0.1)",
579589
border: "1px solid rgba(254,212,105,0.35)",
580590
color: "#FED469",
591+
cursor: isRedirecting ? "not-allowed" : "pointer",
592+
opacity: isRedirecting ? 0.5 : 1,
581593
}}
582594
>
583-
Top Up
595+
{isRedirecting ? "Redirecting..." : "Top Up"}
584596
</button>
585597
<button
586598
onClick={() => navigate("/account/wallet")}
@@ -708,16 +720,17 @@ export const Header = () => {
708720
value={topUpAmount}
709721
onChange={(val) => setTopUpAmount(val)}
710722
min={10000}
723+
max={500_000_000}
711724
step={10000}
712725
formatter={(v) => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
713726
parser={(v) => Number(v?.replace(/,/g, "") ?? 0) as unknown as 10000}
714727
style={{ width: "100%" }}
715-
placeholder="Minimum 10,000 VND"
728+
placeholder="10,000 – 500,000,000 VND"
716729
/>
717730
</div>
718731
<button
719732
onClick={handleTopUp}
720-
disabled={checkoutMutation.isPending}
733+
disabled={checkoutMutation.isPending || isRedirecting}
721734
style={{
722735
width: "100%",
723736
padding: "9px 0",
@@ -727,11 +740,11 @@ export const Header = () => {
727740
color: "#FED469",
728741
fontSize: "14px",
729742
fontWeight: 700,
730-
cursor: checkoutMutation.isPending ? "not-allowed" : "pointer",
743+
cursor: (checkoutMutation.isPending || isRedirecting) ? "not-allowed" : "pointer",
731744
fontFamily: "inherit",
732745
}}
733746
>
734-
{checkoutMutation.isPending ? "Redirecting..." : "Proceed to Checkout"}
747+
{(checkoutMutation.isPending || isRedirecting) ? "Redirecting..." : "Proceed to Checkout"}
735748
</button>
736749
</div>
737750
</Modal>

frontend/src/pages/account/WalletPage.tsx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ArrowRightOutlined, WalletOutlined } from "@ant-design/icons";
1+
import { ArrowRightOutlined } from "@ant-design/icons";
22
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
33
import {
44
DatePicker,
@@ -107,8 +107,11 @@ const WalletPage = () => {
107107
const [searchParams, setSearchParams] = useSearchParams();
108108
const queryClient = useQueryClient();
109109

110+
const activeTab = searchParams.get("tab") ?? "deposits";
111+
110112
const [topUpOpen, setTopUpOpen] = useState(false);
111113
const [amount, setAmount] = useState<number | null>(null);
114+
const [isRedirecting, setIsRedirecting] = useState(false);
112115

113116
// Top-up history filters
114117
const [historyStatus, setHistoryStatus] = useState<TopUpOrderStatus | undefined>(undefined);
@@ -141,7 +144,7 @@ const WalletPage = () => {
141144
} else if (status === "cancelled") {
142145
message.warning("Top-up cancelled.");
143146
}
144-
setSearchParams({}, { replace: true });
147+
setSearchParams(activeTab !== "deposits" ? { tab: activeTab } : {}, { replace: true });
145148
}, []);
146149

147150
const { data: wallet, isLoading: walletLoading } = useQuery({
@@ -159,6 +162,8 @@ const WalletPage = () => {
159162
const checkoutMutation = useMutation({
160163
mutationFn: (amt: number) => paymentApi.createTopUpOrder(amt),
161164
onSuccess: (data) => {
165+
setIsRedirecting(true);
166+
setTopUpOpen(false);
162167
submitSePayForm(data);
163168
},
164169
onError: (err: Error) => {
@@ -171,6 +176,10 @@ const WalletPage = () => {
171176
message.error("Minimum top-up amount is 10,000 VND");
172177
return;
173178
}
179+
if (amount > 500_000_000) {
180+
message.error("Maximum top-up amount is 500,000,000 VND");
181+
return;
182+
}
174183
checkoutMutation.mutate(amount);
175184
};
176185

@@ -267,7 +276,8 @@ const WalletPage = () => {
267276

268277
<div style={{ marginTop: "20px" }}>
269278
<button
270-
onClick={() => setTopUpOpen(true)}
279+
onClick={() => !isRedirecting && setTopUpOpen(true)}
280+
disabled={isRedirecting}
271281
style={{
272282
padding: "7px 18px",
273283
background: "rgba(254,212,105,0.1)",
@@ -276,11 +286,12 @@ const WalletPage = () => {
276286
color: "#FED469",
277287
fontSize: "13px",
278288
fontWeight: 600,
279-
cursor: "pointer",
289+
cursor: isRedirecting ? "not-allowed" : "pointer",
280290
fontFamily: "inherit",
291+
opacity: isRedirecting ? 0.5 : 1,
281292
}}
282293
>
283-
Top Up
294+
{isRedirecting ? "Redirecting..." : "Top Up"}
284295
</button>
285296
</div>
286297
</div>
@@ -318,24 +329,25 @@ const WalletPage = () => {
318329
marginBottom: "6px",
319330
}}
320331
>
321-
Amount (VND)
332+
Amount (1 VND {"->"} 1 USD)
322333
</div>
323334
<InputNumber
324335
value={amount}
325336
onChange={(val) => setAmount(val)}
326337
min={10000}
338+
max={500_000_000}
327339
step={10000}
328340
formatter={(v) => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
329341
parser={(v) =>
330342
Number(v?.replace(/,/g, "") ?? 0) as unknown as 10000
331343
}
332344
style={{ width: "100%" }}
333-
placeholder="Minimum 10,000 VND"
345+
placeholder="10,000 – 500,000,000 VND"
334346
/>
335347
</div>
336348
<button
337349
onClick={handleProceedToCheckout}
338-
disabled={checkoutMutation.isPending}
350+
disabled={checkoutMutation.isPending || isRedirecting}
339351
style={{
340352
width: "100%",
341353
padding: "9px 0",
@@ -345,18 +357,19 @@ const WalletPage = () => {
345357
color: "#FED469",
346358
fontSize: "14px",
347359
fontWeight: 700,
348-
cursor: checkoutMutation.isPending ? "not-allowed" : "pointer",
360+
cursor: (checkoutMutation.isPending || isRedirecting) ? "not-allowed" : "pointer",
349361
fontFamily: "inherit",
350362
}}
351363
>
352-
{checkoutMutation.isPending ? "Redirecting..." : "Proceed to Checkout"}
364+
{(checkoutMutation.isPending || isRedirecting) ? "Redirecting..." : "Proceed to Checkout"}
353365
</button>
354366
</div>
355367
</Modal>
356368

357369
{/* ── History tabs ─────────────────────────────── */}
358370
<Tabs
359-
defaultActiveKey="deposits"
371+
activeKey={activeTab}
372+
onChange={(key) => setSearchParams({ tab: key }, { replace: true })}
360373
style={{ color: "#fff" }}
361374
items={[
362375
{

0 commit comments

Comments
 (0)