npm package: @raba7ni/raba7ni
Description: Official SDK for the Raba7ni Platform Developer API - Loyalty, Cashback & Coins
Author: Raba7ni
Latest version: 2.1.1

# @raba7ni/raba7ni v2.1.1

Official TypeScript SDK for the Raba7ni Platform Developer API.

[![npm version](https://badge.fury.io/js/%40raba7ni%2Fraba7ni.svg)](https://www.npmjs.com/package/@raba7ni/raba7ni)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
[![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)

## What's New in v2.0

- **Product-based API structure** - Separate endpoints for Loyalty, Cashback, and Coins
- **Internal member token management** - Automatic token handling after authentication
- **Points & Stamps operations** - Earn, redeem, balance, and history
- **Cashback operations** - Full cashback earn/redeem workflow
- **Coins operations** - Wallet management, issue/spend coins
- **Member authentication** - OTP-based auth for customer-facing apps

## Installation

```bash
npm install @raba7ni/raba7ni
```

## Quick Start

```typescript
import { Raba7niSDK } from "@raba7ni/raba7ni";

const sdk = new Raba7niSDK({
  appId: "app_your_app_id",
  apiKey: "dev_your_api_key",
});

// Test connection
const scopes = await sdk.testConnection();
console.log("Connected! Scopes:", scopes.scopes);

// Earn points for a member (identify by phone, email, or memberId)
const result = await sdk.earnPoints(5, 25.0, { phone: "+21612345678" });
console.log("Points earned:", result.points_earned);
```

## Configuration

| Option       | Type     | Default                   | Description                         |
| ------------ | -------- | ------------------------- | ----------------------------------- |
| `appId`      | `string` | _required_                | Application ID (starts with `app_`) |
| `apiKey`     | `string` | _required_                | API Key (starts with `dev_`)        |
| `baseUrl`    | `string` | `https://app.raba7ni.com` | API base URL                        |
| `timeout`    | `number` | `30000`                   | Request timeout (ms)                |
| `maxRetries` | `number` | `3`                       | Max retry attempts                  |
| `retryDelay` | `number` | `1000`                    | Initial retry delay (ms)            |

---

## Member Authentication

Many operations (redemptions, wallet access) require member authentication. The SDK handles token storage internally.

### Member Management
```typescript
// Check if a member exists (by email or phone)
const check = await sdk.checkMember({ email: "john@example.com" });
// or: await sdk.checkMember({ phone: "+21612345678" });
if (check.exists) {
  console.log("Member found:", check.member);
}

// Register a new member
const newMember = await sdk.registerMember({
  name: "John Doe",
  email: "john@example.com",
  phone: "+21612345678",
});
```

### Request & Verify OTP

```typescript
// Step 1: Request OTP (sent to member's email or phone)
await sdk.requestMemberOTP({ email: "member@example.com" });
// or: await sdk.requestMemberOTP({ phone: '+21612345678' });

// Step 2: Verify OTP (token stored internally)
const auth = await sdk.verifyMemberOTP({
  email: "member@example.com",
  code: "123456",
});
console.log("Authenticated as:", auth.member.name);

// Now authenticated endpoints will work automatically
const wallet = await sdk.getWallet();
```

### Token Management

```typescript
// Check if authenticated
if (sdk.isAuthenticated()) {
  // Can use authenticated endpoints
}

// Manually set token (e.g., from stored session)
sdk.setMemberToken("token_abc123", "2025-12-31T23:59:59Z");

// Clear token (logout)
sdk.clearMemberToken();
// or
await sdk.logoutMember(); // Also invalidates on server
```

---

## Loyalty API

### Points

```typescript
// Earn points from purchase - identify member by phone, email, or memberId
const earn = await sdk.earnPoints(cardId, 25.0, { phone: "+21612345678" }, {
  staff_identifier: "staff_001",
  note: "Coffee purchase",
  reference: "ORDER-123",
});
console.log("Earned:", earn.points_earned, "New balance:", earn.new_balance);
console.log("Is new member:", earn.is_new_member);

// Identify by email instead
const earn2 = await sdk.earnPoints(cardId, 50.0, { email: "john@example.com" });

// Identify by member ID
const earn3 = await sdk.earnPoints(cardId, 30.0, { memberId: 123 });

// Get balance
const balance = await sdk.getPointsBalance(cardId, "+21612345678");
console.log("Current points:", balance.balance);

// Get history
const history = await sdk.getPointsHistory(cardId, "+21612345678", {
  limit: 20,
  offset: 0,
});

// Refund points (for returns/cancellations)
const refund = await sdk.refundPoints(cardId, 25.0, { phone: "+21612345678" }, {
  points_to_deduct: 50,
  reference: "REFUND-123",
});
console.log("Points deducted:", refund.points_deducted);

// Redeem points (requires authentication)
await sdk.verifyMemberOTP({ email: "member@example.com", code: "123456" });
const redeem = await sdk.redeemPoints(cardId, rewardId);
console.log("Redeemed reward:", redeem.reward.name);
```

### Stamps

```typescript
// Add stamps
const stamp = await sdk.addStamps(cardId, "+21612345678", 1);
console.log("Stamps:", stamp.new_balance, "/", stamp.stamps_required);

// Get balance
const balance = await sdk.getStampsBalance(cardId, "+21612345678");

// Redeem stamps (requires authentication)
const redeem = await sdk.redeemStamps(cardId, rewardId);
```

### Cards & Rewards

```typescript
// List available cards
const { cards } = await sdk.listCards();

// Get card details
const card = await sdk.getCardInfo(cardId);

// Get rewards for a card
const { rewards } = await sdk.listCardRewards(cardId);
```

### Claims

```typescript
// Request claim OTP
await sdk.requestClaimOTP("member@example.com", cardId, rewardId);

// Create claim request
const { claim_request } = await sdk.createClaimRequest({
  member_email: "member@example.com",
  card_id: cardId,
  reward_id: rewardId,
  verification_code: "123456",
  member_note: "Please deliver to office",
});

// Approve/Reject (staff operations)
await sdk.approveClaimRequest(claim_request.id, "Approved!");
await sdk.rejectClaimRequest(claim_request.id, "Out of stock");
```

### Referrals

```typescript
// Get stats
const stats = await sdk.getReferralStats(cardId);

// Validate code
const result = await sdk.validateReferralCode("REF123ABC", cardId);
if (result.is_valid) {
  console.log("Referred by:", result.referrer?.name);
}

// Apply referral
await sdk.applyReferral("REF123ABC", cardId, "newbie@example.com");
```

---

## Cashback API

```typescript
// Earn cashback from purchase - identify member by phone, email, or memberId
const earn = await sdk.earnCashback(cardId, 100.0, { phone: "+21612345678" }, {
  reference: "ORDER-123",
});
console.log(
  "Cashback earned:",
  earn.cashback_earned,
  "(",
  earn.cashback_percentage,
  "%)"
);
console.log("Is new member:", earn.is_new_member);

// Identify by email instead
const earn2 = await sdk.earnCashback(cardId, 50.0, { email: "john@example.com" });

// Get balance
const balance = await sdk.getCashbackBalance(cardId, "+21612345678");

// Get history
const history = await sdk.getCashbackHistory(cardId, "+21612345678");

// Refund cashback (for returns/cancellations)
const refund = await sdk.refundCashback(cardId, 50.0, { phone: "+21612345678" }, {
  cashback_to_deduct: 2.5,
  reference: "REFUND-123",
});
console.log("Cashback deducted:", refund.cashback_deducted);

// Redeem cashback (requires authentication + PIN)
await sdk.verifyMemberOTP({ email: "member@example.com", code: "123456" });
const redeem = await sdk.redeemCashback(cardId, 10.0, "1234");

// List cashback cards
const { cards } = await sdk.listCashbackCards();
```

---

## Coins API

### Wallet Operations (Authenticated)

```typescript
// Must authenticate first
await sdk.verifyMemberOTP({ email: "member@example.com", code: "123456" });

// Get wallet balance
const wallet = await sdk.getWallet();
console.log("Coins:", wallet.wallet.coin_balance);
console.log("Value in TND:", wallet.wallet.balance_in_tnd);

// Get QR code for receiving coins (requires PIN setup)
const qr = await sdk.getWalletQR();
console.log("QR Data:", qr.qr_data);

// Get transaction history
const history = await sdk.getWalletHistory({ limit: 50 });
```

### Coin Operations

```typescript
// Issue coins to member (from purchase) - identify by phone, email, or memberId
const issue = await sdk.issueCoins(clubId, 50.0, { phone: "+21612345678" }, {
  campaign_id: 5,
  reference: "ORDER-123",
});
console.log("Coins issued:", issue.coins_issued);
console.log("Campaign bonus:", issue.campaign_bonus);
console.log("Is new member:", issue.is_new_member);

// Identify by email instead
const issue2 = await sdk.issueCoins(clubId, 30.0, { email: "john@example.com" });

// Refund coins (for returns/cancellations)
const refund = await sdk.refundCoins(clubId, 50.0, { phone: "+21612345678" }, {
  coins_to_deduct: 25,
  reference: "REFUND-123",
});
console.log("Coins deducted:", refund.coins_deducted);

// Spend coins (requires authentication + PIN)
await sdk.verifyMemberOTP({ email: "member@example.com", code: "123456" });
const spend = await sdk.spendCoins(clubId, 100, "1234");
console.log("Spent:", spend.coins_spent, "= TND", spend.amount_in_tnd);

// List participating clubs
const { clubs } = await sdk.listCoinsClubs();

// List active campaigns
const campaigns = await sdk.listCoinsCampaigns(clubId);
```

---

## Webhook Handling

```typescript
import { WebhookHandler } from "@raba7ni/raba7ni";

const handler = new WebhookHandler("your_webhook_secret");

// Express example
app.post("/webhooks", async (req, res) => {
  const signature = req.headers["x-webhook-signature"] as string;
  const payload = JSON.stringify(req.body);

  try {
    await handler.handleWebhook(payload, signature, {
      onMemberJoined: async (data) => {
        console.log("New member:", data.member.name);
      },
      onClaimRequestCreated: async (data) => {
        console.log("New claim:", data.claim_request.id);
      },
      onClaimRequestProcessed: async (data) => {
        console.log("Claim processed:", data.status);
      },
    });
    res.status(200).send("OK");
  } catch (error) {
    res.status(400).send("Invalid webhook");
  }
});
```

---

## Error Handling

```typescript
import {
  Raba7niError,
  AuthenticationError,
  RateLimitError,
  ValidationError,
  MemberTokenRequiredError,
} from "@raba7ni/raba7ni";

try {
  await sdk.redeemPoints(5, 10);
} catch (error) {
  if (error instanceof MemberTokenRequiredError) {
    console.error("Please authenticate first");
  } else if (error instanceof AuthenticationError) {
    console.error("Invalid credentials");
  } else if (error instanceof RateLimitError) {
    console.error("Rate limited, retry after:", error.retryAfter);
  } else if (error instanceof ValidationError) {
    console.error("Validation errors:", error.validationErrors);
  } else if (error instanceof Raba7niError) {
    console.error("API error:", error.message, error.code);
  }
}
```

---

## Rate Limiting

```typescript
const info = sdk.getRateLimitInfo();
console.log("Hourly remaining:", info.hourlyRemaining);
console.log("Daily remaining:", info.dailyRemaining);
```

---

## Utilities

```typescript
import {
  normalizePhoneNumber,
  validateEmail,
  validateCardId,
} from "@raba7ni/raba7ni";

normalizePhoneNumber("+1 (555) 123-4567"); // '+15551234567'
validateEmail("test@example.com"); // true
validateCardId("5"); // 5 (number)
```

---

## TypeScript Types

All types are exported:

```typescript
import type {
  Member,
  Card,
  Reward,
  EarnPointsResponse,
  WalletResponse,
  CoinCampaign,
  // ... and many more
} from "@raba7ni/raba7ni";
```

---

## Migration from v1.x

### Breaking Changes

1. **Legacy methods removed**: `getMemberDetails()`, `findOrCreateMember()` - use product-specific endpoints
2. **New auth flow**: Use `requestMemberOTP()` + `verifyMemberOTP()` for member auth
3. **Internal token storage**: Token is stored in SDK after `verifyMemberOTP()`

### Migration Examples

```typescript
// v1.x
const details = await sdk.getMemberDetails(cardId, phone);

// v2.0
const balance = await sdk.getPointsBalance(cardId, phone);
// or for authenticated member data:
await sdk.verifyMemberOTP({ phone, code: "123456" });
const profile = await sdk.getMemberProfile();
```



## Requirements

- Node.js 18+
- TypeScript 5.0+ (for TypeScript users)

## Support

- **Documentation**: [https://app.raba7ni.com/en-us/docs](https://app.raba7ni.com/en-us/docs)

## License

MIT © Raba7ni