useAutoRenewal
Overview
useAutoRenewal is a React hook for managing subscription auto-renewal status across multiple payment providers (Stripe, LemonSqueezy, Polar). It provides automatic provider detection, optimistic updates with rollback on error, toast notifications, and related query invalidation for data consistency.
Source: template/hooks/use-auto-renewal.ts
Signature
function useAutoRenewal(options: UseAutoRenewalOptions): UseAutoRenewalReturn
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
options | UseAutoRenewalOptions | Yes | Configuration object for the hook. |
UseAutoRenewalOptions
| Property | Type | Default | Description |
|---|---|---|---|
subscriptionId | string | -- | Required. The subscription ID to manage auto-renewal for. |
enabled | boolean | true | Whether to enable the query. Set to false to disable automatic fetching. |
onSuccess | (data: AutoRenewalStatus) => void | -- | Callback fired when auto-renewal status is successfully loaded. Only fires on transitions to success. |
onError | (error: AutoRenewalError) => void | -- | Callback fired when loading auto-renewal status fails. Only fires on transitions to error. |
onUpdateSuccess | (data: UpdateAutoRenewalResponse) => void | -- | Callback fired when auto-renewal is successfully updated. |
onUpdateError | (error: AutoRenewalError) => void | -- | Callback fired when updating auto-renewal fails. |
Return Values
Data
| Property | Type | Description |
|---|---|---|
autoRenewalStatus | AutoRenewalStatus | undefined | The full auto-renewal status object from the API. |
autoRenewal | boolean | undefined | Whether auto-renewal is currently enabled. |
cancelAtPeriodEnd | boolean | Whether the subscription will cancel at the end of the current period. Defaults to false. |
endDate | string | null | The subscription end date, or null if not set. |
paymentProvider | PaymentProvider | The active payment provider (detected automatically or from user selection). |
Query Status
| Property | Type | Description |
|---|---|---|
isLoading | boolean | true while the initial status fetch is in progress. |
isError | boolean | true if the status fetch failed. |
isSuccess | boolean | true if the status fetch completed successfully. |
error | AutoRenewalError | null | The error from the status fetch, if any. |
Mutation Status
| Property | Type | Description |
|---|---|---|
isUpdating | boolean | true while an auto-renewal update is in progress. |
updateError | AutoRenewalError | null | The error from the most recent update attempt. |
isUpdateSuccess | boolean | true if the most recent update succeeded. |
Actions
| Property | Type | Description |
|---|---|---|
refetch | () => void | Manually refetch the auto-renewal status. |
updateAutoRenewal | (enabled: boolean) => void | Update auto-renewal status. Fire-and-forget. |
updateAutoRenewalAsync | (enabled: boolean) => Promise<UpdateAutoRenewalResponse> | Async version that returns a Promise. |
enableAutoRenewal | () => void | Convenience method to enable auto-renewal. |
disableAutoRenewal | () => void | Convenience method to disable auto-renewal. |
toggleAutoRenewal | () => void | Toggle the current auto-renewal state. Only works when autoRenewal is not undefined. |
Cache Management
| Property | Type | Description |
|---|---|---|
invalidateCache | () => void | Mark the auto-renewal cache as stale and trigger a refetch. |
clearCache | () => void | Completely remove the auto-renewal cache. |
Type Definitions
AutoRenewalStatus
interface AutoRenewalStatus {
subscriptionId: string;
autoRenewal: boolean;
cancelAtPeriodEnd: boolean;
endDate: string | null;
paymentProvider?: string;
}
UpdateAutoRenewalRequest
interface UpdateAutoRenewalRequest {
subscriptionId: string;
enabled: boolean;
paymentProvider: PaymentProvider;
}
UpdateAutoRenewalResponse
interface UpdateAutoRenewalResponse {
success: boolean;
subscription: {
id: string;
autoRenewal: boolean;
cancelAtPeriodEnd: boolean;
endDate: string | null;
[key: string]: any;
};
message: string;
}
Implementation Details
- Query Key:
['auto-renewal', subscriptionId, paymentProvider](generated byAUTO_RENEWAL_QUERY_KEYfactory function) - Stale Time: 2 minutes
- Garbage Collection Time: 10 minutes
- Retry Logic: Up to 3 retries. Auth errors (401, 403) and not-found (404) errors are not retried.
- Optimistic Updates: When updating, the cache is immediately set to the new value. If the mutation fails, the cache rolls back to the previous snapshot.
- Related Query Invalidation: On successful update, the hook invalidates
['subscriptions'],['user-subscription'], and['billing']query keys. - Provider Detection: The hook uses
useSelectedCheckoutProviderandusePaymentProviderto automatically determine the active payment provider. User selection takes precedence over config defaults. - Callback Transition Detection: The
onSuccessandonErrorcallbacks only fire on state transitions (not on every render), using refs to track previous states. - API Endpoints:
GET /api/payment/{subscriptionId}?provider={provider}-- Fetch auto-renewal statusPATCH /api/payment/{subscriptionId}-- Update auto-renewal status
Usage Examples
Auto-Renewal Toggle Switch
import { useAutoRenewal } from '@/hooks/use-auto-renewal';
function AutoRenewalToggle({ subscriptionId }: { subscriptionId: string }) {
const {
autoRenewal,
isLoading,
isUpdating,
toggleAutoRenewal
} = useAutoRenewal({ subscriptionId });
return (
<Switch
checked={autoRenewal ?? false}
disabled={isLoading || isUpdating}
onCheckedChange={toggleAutoRenewal}
label="Auto-renewal"
/>
);
}
With Callbacks
function SubscriptionSettings({ subscriptionId }: { subscriptionId: string }) {
const {
autoRenewal,
cancelAtPeriodEnd,
endDate,
enableAutoRenewal,
disableAutoRenewal,
isUpdating
} = useAutoRenewal({
subscriptionId,
onSuccess: (data) => {
console.log('Status loaded:', data);
},
onUpdateSuccess: (response) => {
console.log('Updated:', response.message);
},
onUpdateError: (error) => {
console.error('Update failed:', error.message);
}
});
return (
<div>
<p>Auto-renewal: {autoRenewal ? 'Enabled' : 'Disabled'}</p>
{cancelAtPeriodEnd && <p>Cancels at end of period: {endDate}</p>}
<button onClick={enableAutoRenewal} disabled={isUpdating || autoRenewal}>
Enable
</button>
<button onClick={disableAutoRenewal} disabled={isUpdating || !autoRenewal}>
Disable
</button>
</div>
);
}
Related Hooks
useBillingData-- Fetch subscription and payment history data.usePlanStatus-- Access the user's current plan with expiration awareness.usePaymentMethods-- Manage Stripe payment methods.useLemonSqueezySubscription-- LemonSqueezy-specific subscription management.