stripe webhooks. removed payout cron. webhook for when amount is deposited into bank. More communication about payout timelines
This commit is contained in:
@@ -66,8 +66,8 @@ const EarningsStatus: React.FC<EarningsStatusProps> = ({
|
||||
</div>
|
||||
<h6 className="text-success">Earnings Active</h6>
|
||||
<p className="text-muted small mb-3">
|
||||
Your earnings are set up and working. You'll receive payments
|
||||
automatically.
|
||||
Payouts are sent immediately when rentals complete. Funds reach your
|
||||
bank in 2-7 business days.
|
||||
</p>
|
||||
|
||||
<div className="small text-start">
|
||||
|
||||
@@ -47,7 +47,7 @@ const StripeConnectOnboarding: React.FC<StripeConnectOnboardingProps> = ({
|
||||
colorText: "#212529",
|
||||
colorDanger: "#dc3545",
|
||||
fontFamily: "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
|
||||
fontSizeBase: "16px",
|
||||
fontSizeBase: "20px",
|
||||
borderRadius: "8px",
|
||||
spacingUnit: "4px",
|
||||
},
|
||||
@@ -170,9 +170,9 @@ const StripeConnectOnboarding: React.FC<StripeConnectOnboardingProps> = ({
|
||||
style={{ fontSize: "2rem" }}
|
||||
></i>
|
||||
</div>
|
||||
<h6>Automatic</h6>
|
||||
<h6>Instant Payouts</h6>
|
||||
<small className="text-muted">
|
||||
Earnings are processed automatically
|
||||
Transferred when rentals complete
|
||||
</small>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
@@ -182,9 +182,9 @@ const StripeConnectOnboarding: React.FC<StripeConnectOnboardingProps> = ({
|
||||
style={{ fontSize: "2rem" }}
|
||||
></i>
|
||||
</div>
|
||||
<h6>Direct Deposit</h6>
|
||||
<h6>Fast Deposits</h6>
|
||||
<small className="text-muted">
|
||||
Funds go directly to your bank
|
||||
In your bank in 2-7 business days
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,7 +197,7 @@ const StripeConnectOnboarding: React.FC<StripeConnectOnboardingProps> = ({
|
||||
<li>Verify your identity securely</li>
|
||||
<li>Provide bank account details for deposits</li>
|
||||
<li>The setup process takes about 5 minutes</li>
|
||||
<li>Start earning immediately after setup</li>
|
||||
<li>Receive payouts instantly when rentals complete</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ const EarningsDashboard: React.FC = () => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [earningsData, setEarningsData] = useState<EarningsData | null>(null);
|
||||
const [userProfile, setUserProfile] = useState<User | null>(null);
|
||||
const [accountStatus, setAccountStatus] = useState<AccountStatus | null>(null);
|
||||
const [accountStatus, setAccountStatus] = useState<AccountStatus | null>(
|
||||
null
|
||||
);
|
||||
const [showOnboarding, setShowOnboarding] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -75,7 +77,7 @@ const EarningsDashboard: React.FC = () => {
|
||||
);
|
||||
|
||||
const pendingEarnings = completedRentals
|
||||
.filter((rental: Rental) => rental.payoutStatus === "pending")
|
||||
.filter((rental: Rental) => rental.bankDepositStatus !== "paid")
|
||||
.reduce(
|
||||
(sum: number, rental: Rental) =>
|
||||
sum + parseFloat(rental.payoutAmount?.toString() || "0"),
|
||||
@@ -83,7 +85,7 @@ const EarningsDashboard: React.FC = () => {
|
||||
);
|
||||
|
||||
const completedEarnings = completedRentals
|
||||
.filter((rental: Rental) => rental.payoutStatus === "completed")
|
||||
.filter((rental: Rental) => rental.bankDepositStatus === "paid")
|
||||
.reduce(
|
||||
(sum: number, rental: Rental) =>
|
||||
sum + parseFloat(rental.payoutAmount?.toString() || "0"),
|
||||
@@ -231,21 +233,57 @@ const EarningsDashboard: React.FC = () => {
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
className={`badge ${
|
||||
rental.payoutStatus === "completed"
|
||||
? "bg-success"
|
||||
: rental.payoutStatus === "failed"
|
||||
? "bg-danger"
|
||||
: "bg-secondary"
|
||||
}`}
|
||||
>
|
||||
{rental.payoutStatus === "completed"
|
||||
? "Paid"
|
||||
: rental.payoutStatus === "failed"
|
||||
? "Failed"
|
||||
: "Pending"}
|
||||
</span>
|
||||
{(() => {
|
||||
// Determine badge based on bank deposit and payout status
|
||||
let badgeClass = "bg-secondary";
|
||||
let badgeLabel = "Pending";
|
||||
let badgeTooltip =
|
||||
"Waiting for rental to complete or Stripe setup.";
|
||||
|
||||
if (rental.bankDepositStatus === "paid") {
|
||||
badgeClass = "bg-success";
|
||||
badgeLabel = "Deposited";
|
||||
badgeTooltip = rental.bankDepositAt
|
||||
? `Deposited to your bank on ${new Date(
|
||||
rental.bankDepositAt
|
||||
).toLocaleDateString()}`
|
||||
: "Funds deposited to your bank account.";
|
||||
} else if (
|
||||
rental.bankDepositStatus === "failed"
|
||||
) {
|
||||
badgeClass = "bg-danger";
|
||||
badgeLabel = "Deposit Failed";
|
||||
badgeTooltip =
|
||||
"Bank deposit failed. Please check your Stripe dashboard.";
|
||||
} else if (
|
||||
rental.bankDepositStatus === "in_transit"
|
||||
) {
|
||||
badgeClass = "bg-info";
|
||||
badgeLabel = "In Transit to Bank";
|
||||
badgeTooltip =
|
||||
"Funds are on their way to your bank.";
|
||||
} else if (rental.payoutStatus === "completed") {
|
||||
badgeClass = "bg-info";
|
||||
badgeLabel = "Transferred to Stripe";
|
||||
badgeTooltip =
|
||||
"In your Stripe balance. Bank deposit in 2-7 business days.";
|
||||
} else if (rental.payoutStatus === "failed") {
|
||||
badgeClass = "bg-danger";
|
||||
badgeLabel = "Transfer Failed";
|
||||
badgeTooltip =
|
||||
"Transfer failed. We'll retry automatically.";
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`badge ${badgeClass}`}
|
||||
title={badgeTooltip}
|
||||
style={{ cursor: "help" }}
|
||||
>
|
||||
{badgeLabel}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -274,7 +312,7 @@ const EarningsDashboard: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<div className="card">
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">
|
||||
<h5 className="mb-0">Quick Stats</h5>
|
||||
</div>
|
||||
@@ -301,6 +339,46 @@ const EarningsDashboard: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* How Payouts Work */}
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h5 className="mb-0">How Payouts Work</h5>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="d-flex align-items-start mb-3">
|
||||
<span className="badge bg-success me-2">1</span>
|
||||
<div>
|
||||
<strong>Transfer Initiated</strong>
|
||||
<p className="mb-0 small text-muted">
|
||||
Immediate when rental is marked complete
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex align-items-start mb-3">
|
||||
<span className="badge bg-success me-2">2</span>
|
||||
<div>
|
||||
<strong>Funds in Stripe</strong>
|
||||
<p className="mb-0 small text-muted">
|
||||
Instant — view in your Stripe dashboard
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex align-items-start">
|
||||
<span className="badge bg-primary me-2">3</span>
|
||||
<div>
|
||||
<strong>Funds in Bank</strong>
|
||||
<p className="mb-0 small text-muted">
|
||||
2-7 business days (Stripe's schedule)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<p className="small text-muted mb-0">
|
||||
<Link to="/faq#earnings">Learn more in our FAQ</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -70,17 +70,69 @@ const FAQ: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<p className="small text-muted">
|
||||
<strong>Payout Timeline:</strong> Earnings are processed within 2 business
|
||||
days after the rental is completed. Make sure your Stripe account is set
|
||||
up to receive payouts.
|
||||
<strong>Payout Timeline:</strong> Earnings are transferred immediately when the rental is marked complete. Funds typically reach your bank within 2-7 business days. Make sure your Stripe account is set up to receive payouts.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
question: "When will I receive my earnings?",
|
||||
answer:
|
||||
"Earnings are typically processed within 2 business days after a rental is completed. The exact timing depends on your Stripe account settings and your bank's processing times.",
|
||||
answer: (
|
||||
<div>
|
||||
<p>The payout process has three stages:</p>
|
||||
<div className="bg-light rounded p-3 mb-3">
|
||||
<div className="d-flex align-items-start mb-3">
|
||||
<span className="badge bg-success me-2">1</span>
|
||||
<div>
|
||||
<strong>Transfer Initiated</strong>
|
||||
<span className="text-success ms-2">(Immediate)</span>
|
||||
<p className="mb-0 small text-muted">
|
||||
When the rental is marked complete, we immediately transfer your earnings to your Stripe account.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex align-items-start mb-3">
|
||||
<span className="badge bg-success me-2">2</span>
|
||||
<div>
|
||||
<strong>Funds in Stripe</strong>
|
||||
<span className="text-success ms-2">(Instant)</span>
|
||||
<p className="mb-0 small text-muted">
|
||||
The transfer appears in your Stripe dashboard right away.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex align-items-start">
|
||||
<span className="badge bg-primary me-2">3</span>
|
||||
<div>
|
||||
<strong>Funds in Your Bank</strong>
|
||||
<span className="text-primary ms-2">(2-7 business days)</span>
|
||||
<p className="mb-0 small text-muted">
|
||||
Stripe automatically deposits funds to your bank account. Timing depends on your bank and Stripe's payout schedule.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="small text-muted mb-0">
|
||||
<strong>Note:</strong> If you haven't completed Stripe onboarding, payouts are held until you finish setup. Once complete, all pending payouts are processed immediately.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
question: "Why is my payout still pending?",
|
||||
answer: (
|
||||
<div>
|
||||
<p>A payout may show as "pending" for a few reasons:</p>
|
||||
<ul>
|
||||
<li><strong>Rental not yet complete:</strong> The owner needs to mark the rental as complete before the payout is initiated.</li>
|
||||
<li><strong>Stripe onboarding incomplete:</strong> You need to finish setting up your Stripe account. Visit your <Link to="/earnings">Earnings Dashboard</Link> to complete setup.</li>
|
||||
<li><strong>Processing:</strong> Payouts are initiated immediately but may take a moment to process.</li>
|
||||
</ul>
|
||||
<p className="small text-muted mb-0">
|
||||
If a payout fails for any reason, we automatically retry daily until it succeeds.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
question: "How do I set up my account to receive payments?",
|
||||
|
||||
@@ -312,20 +312,6 @@ const Profile: React.FC = () => {
|
||||
alert("Thank you for your review!");
|
||||
};
|
||||
|
||||
const handleCompleteClick = async (rental: Rental) => {
|
||||
try {
|
||||
await rentalAPI.markAsCompleted(rental.id);
|
||||
setSelectedRentalForReview(rental);
|
||||
setShowReviewRenterModal(true);
|
||||
fetchRentalHistory(); // Refresh rental history
|
||||
} catch (err: any) {
|
||||
alert(
|
||||
"Failed to mark rental as completed: " +
|
||||
(err.response?.data?.error || err.message)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReviewRenterSuccess = () => {
|
||||
fetchRentalHistory(); // Refresh to show updated review status
|
||||
};
|
||||
|
||||
@@ -215,7 +215,6 @@ export const rentalAPI = {
|
||||
getPendingRequestsCount: () => api.get("/rentals/pending-requests-count"),
|
||||
updateRentalStatus: (id: string, status: string) =>
|
||||
api.put(`/rentals/${id}/status`, { status }),
|
||||
markAsCompleted: (id: string) => api.post(`/rentals/${id}/mark-completed`),
|
||||
reviewRenter: (id: string, data: any) =>
|
||||
api.post(`/rentals/${id}/review-renter`, data),
|
||||
reviewItem: (id: string, data: any) =>
|
||||
|
||||
@@ -146,6 +146,11 @@ export interface Rental {
|
||||
payoutStatus?: "pending" | "completed" | "failed" | null;
|
||||
payoutProcessedAt?: string;
|
||||
stripeTransferId?: string;
|
||||
// Bank deposit tracking (Stripe payout to owner's bank)
|
||||
bankDepositStatus?: "pending" | "in_transit" | "paid" | "failed" | "canceled" | null;
|
||||
bankDepositAt?: string;
|
||||
stripePayoutId?: string;
|
||||
bankDepositFailureCode?: string;
|
||||
intendedUse?: string;
|
||||
rating?: number;
|
||||
review?: string;
|
||||
|
||||
Reference in New Issue
Block a user