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 |
|---|---|---|---|---|
|
|
GET |
Get user credits balance |
|
|
|
|
GET |
Get credits transaction history |
|
|
|
|
POST |
Redeem credits for discount codes |
|
|
CashFunds Endpoints
|
Endpoint |
Method |
Purpose |
Key Parameters |
Response Data |
|---|---|---|---|---|
|
|
GET |
Get CashFunds balance |
|
|
|
|
GET |
Get discount codes history |
|
|
|
|
POST |
Redeem CashFunds for discounts |
|
|
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.