Skip to content

Commit 3804129

Browse files
authored
refactor!: streamline useIAP purchase handling (#3012)
## Summary - remove `currentPurchase` / `currentPurchaseError` from `useIAP` in favor of `onPurchaseSuccess` and `onPurchaseError` - refresh tests, mocks, and docs to align with the leaner hook API ## Testing - yarn test src/__tests__/hooks/useIAP.test.ts --watchAll=false <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Added optional onPurchaseSuccess callback in useIAP to receive purchase details on successful transactions. - Breaking Changes - Removed currentPurchase and currentPurchaseError from useIAP, along with their clear functions. Migrate to the onPurchaseSuccess callback for purchase handling. - Test/mocking surface adjusted accordingly. - Documentation - Updated commit message guidelines to follow the Angular Conventional Commits format. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 1b9bbfb commit 3804129

File tree

5 files changed

+25
-56
lines changed

5 files changed

+25
-56
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
9+
### Breaking
10+
11+
- JS: `useIAP` no longer returns `currentPurchase`, `currentPurchaseError`, or the associated clear helpers; consumers should rely on the `onPurchaseSuccess` / `onPurchaseError` callbacks moving forward.
12+
713
## [14.3.5]
814

915
### Changed

CLAUDE.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,17 @@ Access these from the Run and Debug panel (⌘⇧D) in VSCode.
168168

169169
### Commit Message Convention
170170

171-
- **With tag**: Use lowercase after tag (e.g., `feat: add new feature`, `fix: resolve bug`)
172-
- **Without tag**: Start with uppercase (e.g., `Add new feature`, `Fix critical bug`)
173-
- Common tags: `feat:`, `fix:`, `docs:`, `style:`, `refactor:`, `test:`, `chore:`
171+
This repository follows the **Angular Conventional Commits** format:
172+
173+
```
174+
<type>(<scope>): <subject>
175+
```
176+
177+
- `type` is one of: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`.
178+
- `scope` is optional but encouraged (e.g., `hooks`, `android`).
179+
- `subject` is imperative, lowercase, ≤ ~50 chars, and has no trailing period.
180+
- Wrap commit bodies at ~72 columns; include `BREAKING CHANGE:` / `Closes #123` footers when needed.
181+
- Examples: `feat(auth): add refresh token support`, `chore(ci): bump workflows`.
174182

175183
### Code Style
176184

example/jest.setup.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ jest.mock('../src/index', () => ({
7474
products: [],
7575
subscriptions: [],
7676
availablePurchases: [],
77-
currentPurchase: undefined,
78-
currentPurchaseError: undefined,
7977
initConnectionAndListen: jest.fn(() => Promise.resolve(true)),
8078
getProducts: jest.fn(() => Promise.resolve([])),
8179
getSubscriptions: jest.fn(() => Promise.resolve([])),

src/__tests__/hooks/useIAP.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ describe('hooks/useIAP (renderer)', () => {
6464

6565
it('connects on mount and updates state on purchase events', async () => {
6666
let api: any;
67+
const onPurchaseSuccess = jest.fn();
6768
const Harness = () => {
68-
api = useIAP();
69+
api = useIAP({onPurchaseSuccess});
6970
return null;
7071
};
7172

@@ -92,7 +93,7 @@ describe('hooks/useIAP (renderer)', () => {
9293
capturedPurchaseListener?.(purchase);
9394
});
9495
await act(async () => {});
95-
expect(api.currentPurchase?.productId).toBe('p1');
96+
expect(onPurchaseSuccess).toHaveBeenCalledWith(purchase);
9697

9798
// Ensure finishTransaction wrapper works
9899
await act(async () => {

src/hooks/useIAP.ts

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,8 @@ type UseIap = {
5050
promotedProductIdIOS?: string;
5151
subscriptions: ProductSubscription[];
5252
availablePurchases: Purchase[];
53-
currentPurchase?: Purchase;
54-
currentPurchaseError?: PurchaseError;
5553
promotedProductIOS?: Product;
5654
activeSubscriptions: ActiveSubscription[];
57-
clearCurrentPurchase: () => void;
58-
clearCurrentPurchaseError: () => void;
5955
finishTransaction: ({
6056
purchase,
6157
isConsumable,
@@ -114,10 +110,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
114110
const [promotedProductsIOS] = useState<Purchase[]>([]);
115111
const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);
116112
const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);
117-
const [currentPurchase, setCurrentPurchase] = useState<Purchase>();
118113
const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
119-
const [currentPurchaseError, setCurrentPurchaseError] =
120-
useState<PurchaseError>();
121114
const [promotedProductIdIOS] = useState<string>();
122115
const [activeSubscriptions, setActiveSubscriptions] = useState<
123116
ActiveSubscription[]
@@ -168,14 +161,6 @@ export function useIAP(options?: UseIapOptions): UseIap {
168161
subscriptionsRefState.current = subscriptions;
169162
}, [subscriptions]);
170163

171-
const clearCurrentPurchase = useCallback(() => {
172-
setCurrentPurchase(undefined);
173-
}, []);
174-
175-
const clearCurrentPurchaseError = useCallback(() => {
176-
setCurrentPurchaseError(undefined);
177-
}, []);
178-
179164
const getProductsInternal = useCallback(
180165
async (skus: string[]): Promise<void> => {
181166
try {
@@ -313,35 +298,14 @@ export function useIAP(options?: UseIapOptions): UseIap {
313298
});
314299
} catch (err) {
315300
throw err;
316-
} finally {
317-
if (purchase.id === currentPurchase?.id) {
318-
clearCurrentPurchase();
319-
}
320-
if (purchase.id === currentPurchaseError?.productId) {
321-
clearCurrentPurchaseError();
322-
}
323301
}
324302
},
325-
[
326-
currentPurchase?.id,
327-
currentPurchaseError?.productId,
328-
clearCurrentPurchase,
329-
clearCurrentPurchaseError,
330-
],
303+
[],
331304
);
332305

333-
const requestPurchaseWithReset = useCallback(
334-
async (requestObj: RequestPurchaseProps) => {
335-
clearCurrentPurchase();
336-
clearCurrentPurchaseError();
337-
338-
try {
339-
return await requestPurchaseInternal(requestObj);
340-
} catch (error) {
341-
throw error;
342-
}
343-
},
344-
[clearCurrentPurchase, clearCurrentPurchaseError],
306+
const requestPurchase = useCallback(
307+
(requestObj: RequestPurchaseProps) => requestPurchaseInternal(requestObj),
308+
[],
345309
);
346310

347311
// No local restorePurchases; use the top-level helper via returned API
@@ -365,8 +329,6 @@ export function useIAP(options?: UseIapOptions): UseIap {
365329
// Register listeners BEFORE initConnection to avoid race condition
366330
subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(
367331
async (purchase: Purchase) => {
368-
setCurrentPurchaseError(undefined);
369-
setCurrentPurchase(purchase);
370332
// Always refresh subscription state after a purchase event
371333
try {
372334
await getActiveSubscriptionsInternal();
@@ -393,8 +355,6 @@ export function useIAP(options?: UseIapOptions): UseIap {
393355
) {
394356
return;
395357
}
396-
setCurrentPurchase(undefined);
397-
setCurrentPurchaseError(mappedError);
398358
if (optionsRef.current?.onPurchaseError) {
399359
optionsRef.current.onPurchaseError(mappedError);
400360
}
@@ -445,15 +405,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
445405
subscriptions,
446406
finishTransaction,
447407
availablePurchases,
448-
currentPurchase,
449-
currentPurchaseError,
450408
promotedProductIOS,
451409
activeSubscriptions,
452-
clearCurrentPurchase,
453-
clearCurrentPurchaseError,
454410
getAvailablePurchases: getAvailablePurchasesInternal,
455411
fetchProducts: fetchProductsInternal,
456-
requestPurchase: requestPurchaseWithReset,
412+
requestPurchase,
457413
validateReceipt,
458414
restorePurchases: async () => {
459415
try {

0 commit comments

Comments
 (0)