81
81
- Cover: helpers (` src/utils/* ` ), hooks (` src/hooks/* ` ), plugin behavior, error mapping. Avoid testing generated code.
82
82
- Use ` yarn test:ci ` for coverage; add focused tests near changes.
83
83
84
+ ## Hook API Semantics (useIAP)
85
+
86
+ - Most hook methods return ` Promise<void> ` and update internal state. Read results from hook state: ` products ` , ` subscriptions ` , ` availablePurchases ` , etc.
87
+ - Value-returning exceptions in the hook:
88
+ - ` getActiveSubscriptions(ids?) => Promise<ActiveSubscription[]> ` (also updates ` activeSubscriptions ` )
89
+ - ` hasActiveSubscriptions(ids?) => Promise<boolean> `
90
+ - The root API (from ` src/index.ts ` ) is value-returning and can be awaited directly. Prefer root API when not using React state.
91
+
92
+ Example:
93
+
94
+ ``` ts
95
+ const { fetchProducts, products } = useIAP ()
96
+ await fetchProducts ({ skus: [' p1' ] })
97
+ // then read products from state
98
+ ```
99
+
84
100
## CI Checks
85
101
86
102
Run locally before pushing to avoid CI failures:
@@ -97,6 +113,54 @@ yarn test:ci
97
113
yarn ci:check
98
114
```
99
115
116
+ ## Platform-Specific Features
117
+
118
+ - iOS (StoreKit 2): Subscription management, promotional offers, family sharing, refund requests, transaction verification, receipt validation.
119
+ - Android (Play Billing): Multiple SKU purchases, subscription offers, obfuscated account/profile IDs, purchase acknowledgement, product consumption.
120
+ - Android billing uses automatic service reconnection; connection handling is simplified in native code.
121
+
122
+ ## Error Handling
123
+
124
+ - Centralized TS helpers in ` src/utils/* ` and types in ` src/types.ts ` :
125
+ - ` parseErrorStringToJsonObj() ` — parse native error strings into structured objects
126
+ - ` isUserCancelledError() ` — detect user-cancelled operations
127
+ - ` ErrorCode ` enum — normalized cross-platform error codes
128
+ - Native layers map platform errors to the same JSON shape before returning to JS.
129
+
130
+ Error JSON format (from native):
131
+
132
+ ``` json
133
+ {
134
+ "code" : " E_USER_CANCELLED" ,
135
+ "message" : " User cancelled the purchase" ,
136
+ "responseCode" : 1 ,
137
+ "debugMessage" : " User pressed cancel" ,
138
+ "productId" : " com.example.product"
139
+ }
140
+ ```
141
+
142
+ Usage example:
143
+
144
+ ``` ts
145
+ try {
146
+ await requestPurchase ({
147
+ /* ... */
148
+ })
149
+ } catch (e ) {
150
+ const err = parseErrorStringToJsonObj (e )
151
+ if (isUserCancelledError (err )) return
152
+ console .error (' IAP failed:' , err .code , err .message )
153
+ }
154
+ ```
155
+
156
+ ## Troubleshooting
157
+
158
+ - Specs changed but build fails: run ` yarn specs ` to regenerate Nitrogen bridge and rebuild native.
159
+ - React duplication issues in example apps: ensure Metro alias resolves a single ` react ` /` react-native ` (see ` example/metro.config.js ` ).
160
+ - iOS pods: ` cd example/ios && bundle install && bundle exec pod install ` (for Expo example: ` cd example-expo/ios && pod install ` ).
161
+ - Install issues: ` yarn install ` , clear cache ` yarn cache clean ` , or reinstall ` rm -rf node_modules example/node_modules && yarn install ` .
162
+ - Metro cache: ` cd example && yarn start --reset-cache ` .
163
+
100
164
## Nitro Modules
101
165
102
166
### Types
@@ -137,20 +201,20 @@ yarn ci:check
137
201
138
202
``` ts
139
203
// src/specs/RnIap.nitro.ts
140
- export type Purchase = {id: string ; productId: string ; date: Date };
204
+ export type Purchase = { id: string ; productId: string ; date: Date }
141
205
export type Result <T > =
142
- | {kind: ' ok' ; value: T }
143
- | {kind: ' err' ; code: string ; message: string };
206
+ | { kind: ' ok' ; value: T }
207
+ | { kind: ' err' ; code: string ; message: string }
144
208
145
209
export interface RnIapSpec {
146
- init(): Promise <void >;
147
- fetchProducts(ids : string []): Promise <Product []>;
210
+ init(): Promise <void >
211
+ fetchProducts(ids : string []): Promise <Product []>
148
212
requestPurchase(
149
213
id : string ,
150
- opts ? : {quantity? : number },
151
- ): Promise <Result <Purchase >>;
152
- addPurchaseListener(cb : (p : Purchase ) => void ): void ;
153
- getStorefrontIOS? (): string ;
214
+ opts ? : { quantity? : number }
215
+ ): Promise <Result <Purchase >>
216
+ addPurchaseListener(cb : (p : Purchase ) => void ): void
217
+ getStorefrontIOS? (): string
154
218
}
155
219
```
156
220
0 commit comments