@budgetree/rewards-sdk
v0.1.0
Published
Lightweight React Native client for rewards APIs (balance, catalog, earn, redeem).
Maintainers
Readme
react-native-rewards-sdk
Small TypeScript client for a rewards REST API in React Native: balance, catalog, transactions, earn events, and redemptions. Ships as a library you install into your app (this repo is not a standalone runnable app).
Requirements
- React 18+
- React Native 0.72+ (uses the global
fetchavailable in modern RN)
Install
From a local folder (development)
In your React Native project:
npm install /absolute/path/to/sdkOr add to package.json:
"dependencies": {
"react-native-rewards-sdk": "file:../sdk"
}Then install as usual. The package runs prepare on install, which builds dist/.
From this repo before publishing
cd /path/to/sdk
npm install
npm run buildUseful scripts:
| Command | Description |
| -------------- | ------------------------------ |
| npm run build | Compile src/ → dist/ |
| npm run typecheck | Typecheck without emitting |
Quick start
Wrap your app (or a subtree) with RewardsProvider. Pass your API base URL and how to attach auth headers.
import {
RewardsProvider,
useRewardBalance,
useRewardsActions,
} from "react-native-rewards-sdk";
export function App() {
return (
<RewardsProvider
baseUrl="https://api.example.com"
getHeaders={async () => ({
Authorization: `Bearer ${await getAccessToken()}`,
})}
>
<HomeScreen />
</RewardsProvider>
);
}
function HomeScreen() {
const { data, loading, error, refresh } = useRewardBalance();
const { earn, redeem, pending } = useRewardsActions();
if (loading && !data) return <Text>Loading…</Text>;
if (error) return <Text>{error.message}</Text>;
if (!data) return null;
return (
<>
<Text>Points: {data.points}</Text>
<Button title="Refresh" onPress={refresh} />
<Button
title="Earn (example)"
disabled={pending}
onPress={() => earn({ eventName: "app_open" }).then(() => refresh())}
/>
<Button
title="Redeem (example)"
disabled={pending}
onPress={() =>
redeem({ rewardId: "reward_123" }).then(() => refresh())
}
/>
</>
);
}Keep getHeaders stable when possible (for example wrap it in useCallback in the parent) so you do not recreate the client on every render.
Hooks
| Hook | Purpose |
| ------------------------ | ------- |
| useRewardsClient() | Returns the shared RewardsClient (must be inside RewardsProvider). |
| useRewardBalance() | Loads balance: { data, loading, error, refresh }. |
| useRewardCatalog() | Loads catalog array: same shape as balance hook. |
| useGiftCards() | Loads gift cards for GiftCardsScreen. |
| useOrders() | Loads RewardOrder[] for OrdersScreen. |
| useRewardTransactions(limit?) | First page of transactions + nextCursor and loadMore(). |
| useRewardsActions() | { earn, redeem, pending } for mutations without auto-refetch. |
AsyncState from the package is { data, loading, error, refresh }.
Using the client without React
import { RewardsClient } from "react-native-rewards-sdk";
const client = new RewardsClient({
baseUrl: "https://api.example.com",
getHeaders: () => ({ Authorization: "Bearer …" }),
});
const balance = await client.getBalance();
await client.earnEvent({ eventName: "purchase", properties: { sku: "abc" } });Default API shape
The client expects JSON responses. Default paths (all under baseUrl):
| Method | Path | Notes |
| ------ | ---- | ----- |
| GET | /v1/rewards/balance | RewardBalance |
| GET | /v1/rewards/catalog | RewardCatalogItem[] |
| GET | /v1/rewards/gift-cards | GiftCard[] |
| GET | /v1/rewards/orders | RewardOrder[] |
| GET | /v1/rewards/transactions?limit&cursor | { items: RewardTransaction[]; nextCursor?: string } |
| POST | /v1/rewards/earn | Body: EarnEventInput; response treated as RewardBalance |
| POST | /v1/rewards/redeem | Body: RedeemInput; response: RedeemResult |
Types match the field names above (points, costPoints, eventName, rewardId, etc.). If your backend uses different URLs or JSON shapes, override paths and/or adapt your server to these types.
Custom paths
<RewardsProvider
baseUrl="https://api.example.com"
paths={{
balance: "/api/me/points",
catalog: "/api/rewards/shop",
giftCards: "/api/rewards/gift-cards",
orders: "/api/me/orders",
transactions: "/api/me/history",
earn: "/api/events",
redeem: "/api/redeem",
}}
getHeaders={getHeaders}
>
{children}
</RewardsProvider>Export defaultPaths from the package if you want to spread and override only one segment.
Errors
Failed responses throw RewardsRequestError (extends Error):
status— HTTP status codemessage— from JSONmessagewhen present, otherwise status textbody— parsed JSON when possible
import { RewardsRequestError } from "react-native-rewards-sdk";
try {
await client.redeem({ rewardId: "x" });
} catch (e) {
if (e instanceof RewardsRequestError) {
console.warn(e.status, e.body);
}
}Testing
Pass a stub fetchImpl in config to avoid the network:
const client = new RewardsClient({
baseUrl: "https://example.com",
fetchImpl: async () =>
new Response(JSON.stringify({ points: 100 }), { status: 200 }),
});License
MIT
