Rewards

Integrate Rewards

This guide shows you how to integrate CashIn's Rewards endpoints to manage user credits, CashFunds, and redemption systems that turn one-time buyers into repeat advocates.

Credits Endpoints

Endpoint

Method

Purpose

Key Parameters

Response Data

/partner/v1/rewards/credits

GET

Get user credits balance

instance_id

balance (number)

/partner/v1/rewards/credits/history

GET

Get credits transaction history

instance_id, pagination, filters

transactions array, pagination

/partner/v1/rewards/credits/redeem

POST

Redeem credits for discount codes

instance_id, amount, redemption_type, merchant_id

discount_code, new_balance, redemption_id

CashFunds Endpoints

Endpoint

Method

Purpose

Key Parameters

Response Data

/partner/v1/rewards/cashfunds

GET

Get CashFunds balance

instance_id

balance (number)

/partner/v1/rewards/cashfunds/history

GET

Get discount codes history

instance_id, pagination, filters

discount_codes array, pagination

/partner/v1/rewards/cashfunds/redeem

POST

Redeem CashFunds for discounts

instance_id, amount, redemption_type, merchant_id

discount_code, new_balance, redemption_id

Authentication Setup

const headers = {
  'Authorization': 'Bearer sk_live_abc123def456...',
  'Content-Type': 'application/json'
};

Quick Start: Display User Rewards

Get User Credits Balance

async function getUserCredits(instanceId) {
  const response = await fetch('/partner/v1/rewards/credits', {
    method: 'GET',
    headers,
    body: JSON.stringify({
      instance_id: instanceId
    })
  });

  const result = await response.json();
  
  if (result.success) {
    return result.data.balance;
  }
  
  throw new Error(result.message);
}

// Usage
const creditsBalance = await getUserCredits('user-instance-id');
console.log(`User has $${creditsBalance} in credits`);

Get CashFunds Balance

async function getCashFunds(instanceId) {
  const response = await fetch('/partner/v1/rewards/cashfunds', {
    method: 'GET',
    headers,
    body: JSON.stringify({
      instance_id: instanceId
    })
  });

  const result = await response.json();
  
  if (result.success) {
    return result.data.balance;
  }
  
  return 0; // No CashFunds available
}

// Usage
const cashFunds = await getCashFunds('user-instance-id');
console.log(`User has $${cashFunds} in CashFunds`);

Display Complete Rewards Dashboard

Build Rewards Overview

async function buildRewardsDashboard(instanceId) {
  try {
    const [credits, cashFunds, creditsHistory, cashFundsHistory] = await Promise.all([
      getUserCredits(instanceId),
      getCashFunds(instanceId),
      getCreditsHistory(instanceId, { limit: 5 }),
      getCashFundsHistory(instanceId, { limit: 5 })
    ]);

    return {
      balances: {
        credits,
        cashFunds,
        total: credits + cashFunds
      },
      recentActivity: {
        creditsTransactions: creditsHistory.transactions,
        discountCodes: cashFundsHistory.discount_codes
      }
    };
  } catch (error) {
    console.error('Failed to load rewards dashboard:', error);
    return null;
  }
}

// Usage
const dashboard = await buildRewardsDashboard('user-instance-id');
if (dashboard) {
  displayRewardsDashboard(dashboard);
}

Display Rewards in UI

function displayRewardsDashboard(dashboard) {
  // Update balance displays
  document.getElementById('credits-balance').textContent = `$${dashboard.balances.credits}`;
  document.getElementById('cashfunds-balance').textContent = `$${dashboard.balances.cashFunds}`;
  document.getElementById('total-rewards').textContent = `$${dashboard.balances.total}`;

  // Show recent credits activity
  const creditsContainer = document.getElementById('recent-credits');
  creditsContainer.innerHTML = dashboard.recentActivity.creditsTransactions
    .map(transaction => `
      <div class="transaction">
        <span class="type">${transaction.type}</span>
        <span class="amount ${transaction.amount > 0 ? 'positive' : 'negative'}">
          ${transaction.amount > 0 ? '+' : ''}$${Math.abs(transaction.amount)}
        </span>
        <span class="date">${new Date(transaction.created_at).toLocaleDateString()}</span>
      </div>
    `).join('');

  // Show recent discount codes
  const codesContainer = document.getElementById('discount-codes');
  codesContainer.innerHTML = dashboard.recentActivity.discountCodes
    .map(code => `
      <div class="discount-code">
        <span class="code">${code.discount_code}</span>
        <span class="value">$${code.discount_value} off</span>
        <span class="merchant">${code.merchant_name}</span>
        <span class="status ${code.is_active ? 'active' : 'inactive'}">
          ${code.is_active ? 'Active' : 'Used'}
        </span>
      </div>
    `).join('');
}

Transaction History Management

Get Credits History with Filtering

async function getCreditsHistory(instanceId, options = {}) {
  const queryParams = new URLSearchParams({
    instance_id: instanceId,
    limit: options.limit || 20,
    offset: options.offset || 0,
    sortBy: options.sortBy || 'created_at',
    sortOrder: options.sortOrder || 'DESC'
  });

  if (options.status) queryParams.append('status', options.status);
  if (options.search) queryParams.append('search', options.search);

  const response = await fetch(`/partner/v1/rewards/credits/history?${queryParams}`, {
    method: 'GET',
    headers
  });

  const result = await response.json();
  return result.success ? result.data : { transactions: [], pagination: {} };
}

// Usage examples
const allHistory = await getCreditsHistory('user-instance-id');
const completedOnly = await getCreditsHistory('user-instance-id', { status: 'completed' });
const searchResults = await getCreditsHistory('user-instance-id', { search: 'commission' });

Get CashFunds History

async function getCashFundsHistory(instanceId, options = {}) {
  const queryParams = new URLSearchParams({
    instance_id: instanceId,
    limit: options.limit || 20,
    offset: options.offset || 0,
    sortBy: options.sortBy || 'created_at',
    sortOrder: options.sortOrder || 'DESC'
  });

  if (options.status) queryParams.append('status', options.status);
  if (options.search) queryParams.append('search', options.search);

  const response = await fetch(`/partner/v1/rewards/cashfunds/history?${queryParams}`, {
    method: 'GET',
    headers
  });

  const result = await response.json();
  return result.success ? result.data : { discount_codes: [], pagination: {} };
}

Redemption System

Redeem Credits for Discount Code

async function redeemCredits(instanceId, amount, merchantId, notes = '') {
  const response = await fetch('/partner/v1/rewards/credits/redeem', {
    method: 'POST',
    headers,
    body: JSON.stringify({
      instance_id: instanceId,
      redemption_type: 'discount_code',
      amount: amount,
      merchant_id: merchantId,
      notes: notes
    })
  });

  const result = await response.json();
  
  if (result.success) {
    return {
      success: true,
      discountCode: result.data.discount_code,
      newBalance: result.data.new_balance,
      redemptionId: result.data.redemption_id
    };
  }
  
  throw new Error(result.message);
}

// Usage
try {
  const redemption = await redeemCredits('user-instance-id', 25.00, 'merchant-id', 'Store purchase');
  console.log(`Discount code: ${redemption.discountCode}`);
  console.log(`New balance: $${redemption.newBalance}`);
} catch (error) {
  console.error('Redemption failed:', error.message);
}

Redeem CashFunds for Discount Code

async function redeemCashFunds(instanceId, amount, merchantId, notes = '') {
  const response = await fetch('/partner/v1/rewards/cashfunds/redeem', {
    method: 'POST',
    headers,
    body: JSON.stringify({
      instance_id: instanceId,
      redemption_type: 'discount_code',
      amount: amount,
      merchant_id: merchantId,
      notes: notes
    })
  });

  const result = await response.json();
  
  if (result.success) {
    return {
      success: true,
      discountCode: result.data.discount_code,
      newBalance: result.data.new_balance,
      redemptionId: result.data.redemption_id
    };
  }
  
  throw new Error(result.message);
}

Smart Redemption Handler

class RedemptionManager {
  constructor(instanceId) {
    this.instanceId = instanceId;
  }

  async getAvailableBalance() {
    const [credits, cashFunds] = await Promise.all([
      getUserCredits(this.instanceId),
      getCashFunds(this.instanceId)
    ]);

    return { credits, cashFunds, total: credits + cashFunds };
  }

  async redeemOptimal(requestedAmount, merchantId, notes = '') {
    const balances = await this.getAvailableBalance();
    
    if (balances.total < requestedAmount) {
      throw new Error(`Insufficient balance. Available: $${balances.total}, Requested: $${requestedAmount}`);
    }

    // Prefer CashFunds first (merchant gets paid by CashIn)
    if (balances.cashFunds >= requestedAmount) {
      return await redeemCashFunds(this.instanceId, requestedAmount, merchantId, notes);
    }
    
    // Use credits if CashFunds insufficient
    if (balances.credits >= requestedAmount) {
      return await redeemCredits(this.instanceId, requestedAmount, merchantId, notes);
    }
    
    // Split between CashFunds and Credits
    return await this.splitRedemption(requestedAmount, merchantId, notes, balances);
  }

  async splitRedemption(amount, merchantId, notes, balances) {
    const redemptions = [];
    
    // Use all available CashFunds first
    if (balances.cashFunds > 0) {
      const cashFundsRedemption = await redeemCashFunds(
        this.instanceId, 
        balances.cashFunds, 
        merchantId, 
        `${notes} (CashFunds portion)`
      );
      redemptions.push(cashFundsRedemption);
    }
    
    // Use credits for remainder
    const remainingAmount = amount - balances.cashFunds;
    if (remainingAmount > 0) {
      const creditsRedemption = await redeemCredits(
        this.instanceId, 
        remainingAmount, 
        merchantId, 
        `${notes} (Credits portion)`
      );
      redemptions.push(creditsRedemption);
    }
    
    return {
      success: true,
      splitRedemption: true,
      redemptions,
      totalAmount: amount
    };
  }
}

// Usage
const redemptionManager = new RedemptionManager('user-instance-id');

try {
  const result = await redemptionManager.redeemOptimal(35.00, 'merchant-id', 'Purchase discount');
  
  if (result.splitRedemption) {
    console.log('Split redemption completed:', result.redemptions.length, 'codes generated');
  } else {
    console.log('Single redemption:', result.discountCode);
  }
} catch (error) {
  console.error('Redemption failed:', error.message);
}

Partner Autonomy Features

Display Available Deals

async function displayUserDeals(instanceId) {
  // Get user's available deals (this would be a separate endpoint)
  const deals = await getUserAvailableDeals(instanceId);
  
  const dealsContainer = document.getElementById('available-deals');
  dealsContainer.innerHTML = deals.map(deal => `
    <div class="deal-card">
      <h3>${deal.title}</h3>
      <p class="discount">${deal.discount_value}% off</p>
      <p class="merchant">${deal.merchant_name}</p>
      <button onclick="shareDealer('${deal.id}')">Share with Friends</button>
      <button onclick="useDeal('${deal.id}')">Use Deal</button>
    </div>
  `).join('');
}

async function shareDealer(dealId) {
  // Generate sharing link for friends
  const shareLink = await generateShareLink(dealId);
  
  // Show sharing options
  navigator.share({
    title: 'Check out this deal!',
    url: shareLink
  });
}

async function useDeal(dealId) {
  // Redirect to merchant with deal applied
  window.location.href = `/deals/${dealId}/use`;
}

Rewards Integration Widget

class RewardsWidget {
  constructor(containerId, instanceId) {
    this.container = document.getElementById(containerId);
    this.instanceId = instanceId;
    this.init();
  }

  async init() {
    await this.loadRewardsData();
    this.render();
    this.attachEventListeners();
  }

  async loadRewardsData() {
    this.data = await buildRewardsDashboard(this.instanceId);
  }

  render() {
    this.container.innerHTML = `
      <div class="rewards-widget">
        <div class="balances">
          <div class="balance-item">
            <label>Credits</label>
            <span class="amount">$${this.data.balances.credits}</span>
          </div>
          <div class="balance-item">
            <label>CashFunds</label>
            <span class="amount">$${this.data.balances.cashFunds}</span>
          </div>
        </div>
        
        <div class="actions">
          <button id="redeem-btn" ${this.data.balances.total === 0 ? 'disabled' : ''}>
            Redeem Rewards
          </button>
          <button id="history-btn">View History</button>
        </div>
        
        <div class="recent-activity">
          <h4>Recent Activity</h4>
          <div id="activity-list"></div>
        </div>
      </div>
    `;
    
    this.displayRecentActivity();
  }

  displayRecentActivity() {
    const activityList = this.container.querySelector('#activity-list');
    const recentTransactions = this.data.recentActivity.creditsTransactions.slice(0, 3);
    
    activityList.innerHTML = recentTransactions.map(transaction => `
      <div class="activity-item">
        <span class="description">${this.formatTransactionType(transaction.type)}</span>
        <span class="amount ${transaction.amount > 0 ? 'positive' : 'negative'}">
          ${transaction.amount > 0 ? '+' : ''}$${Math.abs(transaction.amount)}
        </span>
      </div>
    `).join('');
  }

  formatTransactionType(type) {
    const typeMap = {
      'COMMISSION_EARNED': 'Commission Earned',
      'PAYOUT_PROCESSED': 'Payout Processed',
      'ADJUSTMENT': 'Balance Adjustment'
    };
    return typeMap[type] || type;
  }

  attachEventListeners() {
    const redeemBtn = this.container.querySelector('#redeem-btn');
    const historyBtn = this.container.querySelector('#history-btn');
    
    redeemBtn.addEventListener('click', () => this.showRedemptionModal());
    historyBtn.addEventListener('click', () => this.showHistoryModal());
  }

  showRedemptionModal() {
    // Create and show redemption modal
    const modal = document.createElement('div');
    modal.className = 'rewards-modal';
    modal.innerHTML = `
      <div class="modal-content">
        <h3>Redeem Rewards</h3>
        <form id="redemption-form">
          <input type="number" id="amount" placeholder="Amount to redeem" max="${this.data.balances.total}" step="0.01" required>
          <select id="merchant" required>
            <option value="">Select merchant...</option>
            <!-- Populate with available merchants -->
          </select>
          <button type="submit">Generate Discount Code</button>
          <button type="button" onclick="this.closest('.rewards-modal').remove()">Cancel</button>
        </form>
      </div>
    `;
    
    document.body.appendChild(modal);
    
    // Handle form submission
    const form = modal.querySelector('#redemption-form');
    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      
      const amount = parseFloat(e.target.amount.value);
      const merchantId = e.target.merchant.value;
      
      try {
        const redemptionManager = new RedemptionManager(this.instanceId);
        const result = await redemptionManager.redeemOptimal(amount, merchantId, 'Widget redemption');
        
        alert(`Success! Discount code: ${result.discountCode || 'Multiple codes generated'}`);
        modal.remove();
        this.loadRewardsData().then(() => this.render());
      } catch (error) {
        alert(`Redemption failed: ${error.message}`);
      }
    });
  }

  showHistoryModal() {
    // Show transaction history in modal
    console.log('Show history modal');
  }
}

// Usage
const rewardsWidget = new RewardsWidget('rewards-container', 'user-instance-id');

Error Handling

Comprehensive Error Management

class RewardsErrorHandler {
  static async handleRewardsRequest(requestFunction) {
    try {
      return await requestFunction();
    } catch (error) {
      return this.handleError(error);
    }
  }

  static handleError(error) {
    if (error.status === 422) {
      return {
        success: false,
        error: 'Insufficient balance',
        userMessage: 'You don\'t have enough rewards balance for this redemption.'
      };
    }
    
    if (error.status === 404) {
      return {
        success: false,
        error: 'User not found',
        userMessage: 'Unable to find your rewards account. Please try signing in again.'
      };
    }
    
    return {
      success: false,
      error: 'Unknown error',
      userMessage: 'Something went wrong. Please try again later.'
    };
  }
}

// Usage
const result = await RewardsErrorHandler.handleRewardsRequest(async () => {
  return await redeemCredits('user-instance-id', 50.00, 'merchant-id');
});

if (!result.success) {
  showErrorMessage(result.userMessage);
}

Complete Integration Example

// Complete rewards system integration
class RewardsSystem {
  constructor(instanceId) {
    this.instanceId = instanceId;
    this.redemptionManager = new RedemptionManager(instanceId);
  }

  async initialize() {
    try {
      // Load user rewards data
      this.dashboard = await buildRewardsDashboard(this.instanceId);
      
      // Display in UI
      this.displayRewards();
      
      // Set up event listeners
      this.setupEventListeners();
      
      console.log('Rewards system initialized successfully');
    } catch (error) {
      console.error('Failed to initialize rewards system:', error);
    }
  }

  displayRewards() {
    displayRewardsDashboard(this.dashboard);
  }

  setupEventListeners() {
    // Redemption buttons
    document.getElementById('redeem-credits-btn').addEventListener('click', () => {
      this.showRedemptionFlow('credits');
    });
    
    document.getElementById('redeem-cashfunds-btn').addEventListener('click', () => {
      this.showRedemptionFlow('cashfunds');
    });
  }

  async showRedemptionFlow(type) {
    // Show redemption interface based on type
    console.log(`Starting ${type} redemption flow`);
  }

  async refresh() {
    this.dashboard = await buildRewardsDashboard(this.instanceId);
    this.displayRewards();
  }
}

// Initialize rewards system on page load
document.addEventListener('DOMContentLoaded', () => {
  const instanceId = localStorage.getItem('cashin_instance_id');
  if (instanceId) {
    const rewardsSystem = new RewardsSystem(instanceId);
    rewardsSystem.initialize();
  }
});

This integration guide enables partner autonomy by allowing businesses to show their users' complete rewards status, transaction history, and redemption options directly in their own interface while leveraging CashIn's powerful rewards infrastructure.