VibeCoding Purchases SDK for React Native and Expo apps
npm install @vibecoding-official/purchasesA React Native and Expo SDK for VibeCoding's in-app purchase and subscription management system.
- ✅ Cross-platform support (iOS, Android, Web)
- ✅ React Native and Expo compatibility (including Expo Go detection)
- ✅ TypeScript support with full type definitions
- ✅ React hooks for easy integration
- ✅ Automatic receipt validation
- ✅ Subscription management
- ✅ Customer info synchronization
- ✅ Error handling and retry logic
- ✅ Platform-aware purchase handling
- ✅ Expo IAP integration for development builds
- ✅ Provider pattern for React applications
``bash`
npm install @vibecoding/purchases
`bash`
yarn add @vibecoding/purchases
`bash`
pnpm add @vibecoding/purchases
For React Native projects, you'll also need:
`bash`
npm install react-native-iap
For Expo projects, you'll also need:
`bash`
expo install expo-iap expo-constants
`typescript
import { VibeCodingPurchasesProvider, useVibeCodingPurchases } from '@vibecoding/purchases';
// Wrap your app with the provider
function App() {
const config = {
apiKey: "your-api-key",
projectId: "your-project-id",
environment: "production", // or 'development'
baseUrl: "https://your-api-url.com" // optional
};
return (
appUserId="user-123" // optional, for automatic login
>
);
}
// Use the hook in your components
function SubscriptionScreen() {
const {
customerInfo,
offerings,
isLoading,
error,
purchasePackage,
purchase, // simplified purchase by package ID
hasEntitlement,
login,
logOut,
canMakePurchases,
isExpoGo
} = useVibeCodingPurchases();
const handlePurchase = async (packageToPurchase) => {
try {
const result = await purchasePackage(packageToPurchase);
console.log('Purchase successful:', result);
} catch (error) {
console.error('Purchase failed:', error);
}
};
const handleSimplePurchase = async (packageId) => {
try {
await purchase(packageId); // Purchase by package ID
console.log('Purchase successful');
} catch (error) {
console.error('Purchase failed:', error);
}
};
if (isLoading) {
return
}
if (isExpoGo) {
return
}
return (
{offerings?.offerings.current && Object.values(offerings.offerings.current).map((offering) => (
offering.availablePackages.map((pkg) => (
key={pkg.identifier}
title={Buy ${pkg.identifier} - ${pkg.product.price} ${pkg.product.currency}}
onPress={() => handlePurchase(pkg)}
/>
))
))}
);
}
`
`typescript
import { VibeCodingPurchases } from "@vibecoding/purchases";
// Configure the SDK
const purchases = VibeCodingPurchases.configure({
apiKey: "your-api-key",
projectId: "your-project-id",
environment: "production",
});
// Login a user
const { customerInfo, created } = await purchases.logIn("user-123");
// Get available offerings
const offerings = await purchases.getOfferings();
// Get customer info
const customerInfo = await purchases.getCustomerInfo();
// Make a purchase using static method
const result = await VibeCodingPurchases.purchase("monthly_premium");
// Check if user is anonymous
const isAnonymous = VibeCodingPurchases.isAnonymous();
// Get current user ID
const userId = VibeCodingPurchases.getUserId();
// Log out
await VibeCodingPurchases.logOut();
`
`typescript
import { VibeCodingPurchases } from "@vibecoding/purchases";
// Initialize with configuration and optional user
const purchases = await VibeCodingPurchases.initialize({
apiKey: "your-api-key",
projectId: "your-project-id",
environment: "production",
appUserId: "user-123", // optional
});
`
`typescript`
interface VibeCodingPurchasesConfiguration {
apiKey: string;
projectId: string;
baseUrl?: string;
environment?: "production" | "development";
}
#### Static Methods
##### VibeCodingPurchases.configure(config)
Configure the SDK with your API credentials. Returns a VibeCodingPurchases instance.
##### VibeCodingPurchases.initialize(config & { appUserId?: string })
Initialize the SDK with configuration and optional user login.
##### VibeCodingPurchases.isAnonymous()
Check if the current user is anonymous.
##### VibeCodingPurchases.getUserId()
Get the current app user ID.
##### VibeCodingPurchases.purchase(packageId)
Purchase a product by package ID (static convenience method).
##### VibeCodingPurchases.logOut()
Log out the current user (static method).
#### Instance Methods
##### purchases.logIn(appUserId)
Log in a specific user. Returns { customerInfo, created }.
##### purchases.getCustomerInfo()
Get current customer information and entitlements.
##### purchases.getOfferings(provider?)
Fetch available products and subscriptions. Optional provider parameter ("apple" | "google").
##### purchases.purchasePackage(package)
Purchase a product or subscription using a Package object.
##### purchases.restorePurchases()
Restore previous purchases (iOS primarily).
##### purchases.getCurrentAppUserId()
Get the current app user ID.
##### purchases.getCachedCustomerInfo()
Get cached customer information without making a network request.
##### purchases.isAnonymous()
Check if the current user is anonymous.
##### purchases.addCustomerInfoUpdateListener(listener)
Add a listener for customer info updates. Returns listener ID.
##### purchases.removeCustomerInfoUpdateListener(listenerId)
Remove a customer info update listener.
#### useVibeCodingPurchases()
Main hook providing access to purchases functionality.
`typescript
const {
// State
customerInfo,
offerings,
isLoading,
error,
purchaseInProgress,
userId,
// Platform info
isExpoGo,
canMakePurchases,
// Methods
login,
logOut,
purchasePackage,
purchase, // simplified purchase by package ID
restorePurchases,
getOfferings,
refreshCustomerInfo,
// Utility methods
hasEntitlement,
isPurchasing,
isAnonymous,
} = useVibeCodingPurchases();
`
#### useEntitlement(entitlementId)
Hook for checking a specific entitlement.
`typescript`
const {
isActive,
productIdentifier,
expirationDate,
purchaseDate,
identifier,
} = useEntitlement("premium");
#### useSubscriptionStatus()
Hook for subscription status information.
`typescript`
const { hasActiveSubscription, activeSubscriptions, allEntitlements } =
useSubscriptionStatus();
The SDK exports comprehensive TypeScript types:
- CustomerInfo - Customer subscription and entitlement informationOffering
- - Available products grouped by offeringOfferingsResponse
- - Complete offerings response structurePackage
- - Individual purchasable packageProduct
- - Product information (price, currency, identifier)Entitlement
- - Individual entitlement with status and datesLoginResponse
- - Response from login API callPurchaseResponse
- - Response from purchase API callVibeCodingPurchasesConfiguration
- - SDK configuration interfaceVibeCodingPurchasesError
- - Custom error classVibeCodingPurchasesErrorCode
- - Error code enumUseVibeCodingPurchasesResult
- - Hook return typeReactNativeIAPPurchase
- - React Native IAP purchase interface
The SDK includes comprehensive error handling:
`typescript
import {
VibeCodingPurchasesError,
VibeCodingPurchasesErrorCode,
} from "@vibecoding/purchases";
try {
await VibeCodingPurchases.purchase("monthly_premium");
} catch (error) {
if (error instanceof VibeCodingPurchasesError) {
switch (error.code) {
case VibeCodingPurchasesErrorCode.PURCHASE_CANCELLED_ERROR:
console.log("User cancelled the purchase");
break;
case VibeCodingPurchasesErrorCode.NETWORK_ERROR:
console.log("Network error occurred");
break;
case VibeCodingPurchasesErrorCode.PLATFORM_NOT_SUPPORTED_ERROR:
console.log("Platform not supported for purchases");
break;
case VibeCodingPurchasesErrorCode.USER_NOT_LOGGED_IN_ERROR:
console.log("User needs to be logged in");
break;
case VibeCodingPurchasesErrorCode.CONFIGURATION_ERROR:
console.log("SDK configuration error");
break;
case VibeCodingPurchasesErrorCode.PURCHASE_IN_PROGRESS_ERROR:
console.log("Another purchase is already in progress");
break;
case VibeCodingPurchasesErrorCode.PRODUCT_NOT_AVAILABLE_ERROR:
console.log("Product not available");
break;
// Handle other error codes...
}
}
}
`
- UNKNOWN_ERROR - Unknown error occurredPURCHASE_CANCELLED_ERROR
- - User cancelled the purchaseSTORE_PROBLEM_ERROR
- - Store/platform errorNETWORK_ERROR
- - Network connectivity issueINVALID_CREDENTIALS_ERROR
- - Invalid API credentialsINVALID_APP_USER_ID_ERROR
- - Invalid user ID providedCONFIGURATION_ERROR
- - SDK configuration issueUSER_NOT_LOGGED_IN_ERROR
- - User needs to be logged inRECEIPT_VALIDATION_ERROR
- - Receipt validation failedPURCHASE_IN_PROGRESS_ERROR
- - Another purchase is in progressPRODUCT_NOT_AVAILABLE_ERROR
- - Product not found or unavailablePLATFORM_NOT_SUPPORTED_ERROR
- - Platform doesn't support purchases
- iOS: Full support via App Store (uses expo-iap in Expo development builds)
- Android: Full support via Google Play (uses expo-iap in Expo development builds)
- Web: Limited support - redirects to web-based checkout (Stripe)
- Expo Go: Not supported - displays helpful error messages and guidance
- React Native: Full support with react-native-iap integration
The SDK automatically detects the platform and adjusts behavior accordingly:
`typescript
import {
getPlatform,
isExpo,
isExpoGo,
isReactNative,
} from "@vibecoding/purchases";
const platform = getPlatform(); // "ios" | "android" | "web"
const isExpoEnvironment = isExpo(); // boolean
const isRunningInExpoGo = isExpoGo(); // boolean
const isReactNativeApp = isReactNative(); // boolean
`
For advanced use cases, you can access the API client directly:
`typescript
import { VibeCodingPurchases } from "@vibecoding/purchases";
const purchases = VibeCodingPurchases.getSharedInstance();
const apiClient = purchases.apiClient;
// Make direct API calls
const customerInfo = await apiClient.getCustomerInfo("user-123");
const offerings = await apiClient.getOfferings("apple");
const loginResult = await apiClient.login("user-123");
const purchaseResult = await apiClient.processPurchase(
"user-123",
"product-id",
"receipt-data",
"transaction-id" // optional, iOS only
);
`
For React Native projects, there are additional convenience exports:
`typescript
import {
initializeVibeCodingPurchases,
usePurchases, // alias for useVibeCodingPurchases
} from "@vibecoding/purchases/react-native";
// Initialize with React Native defaults
const purchases = await initializeVibeCodingPurchases(
{
apiKey: "your-key",
projectId: "your-project",
},
"user-123" // optional auto-login
);
`
The SDK also exports a convenience object with static methods:
`typescript
import { VibeCodingPurchasesSDK } from "@vibecoding/purchases";
// All static methods available
const purchases = VibeCodingPurchasesSDK.configure(config);
const instance = VibeCodingPurchasesSDK.getSharedInstance();
await VibeCodingPurchasesSDK.initialize(config);
const isAnon = VibeCodingPurchasesSDK.isAnonymous();
const userId = VibeCodingPurchasesSDK.getUserId();
await VibeCodingPurchasesSDK.purchase("package-id");
await VibeCodingPurchasesSDK.logOut();
`
`bashInstall dependencies
npm install
$3
`bash
npm test
`$3
`bash
npm run lint
npm run lint:fix
`Contributing
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add some amazing feature')
4. Push to the branch (git push origin feature/amazing-feature`)This project is licensed under the MIT License - see the LICENSE file for details.
For support, email support@vibecoding.com or create an issue in the GitHub repository.