# Comeback Cash POS Integration Guide

> **Version**: 1.0
> **Last Updated**: 2026-01-05
> **Base URL**: `https://{environment}.buyerkiosk.com/api/{typeNum}/comeback-cash`

---

## Overview

The Comeback Cash system allows stores to issue promotional coupons when customers complete qualifying transactions. Coupons can be earned on either:

- **Buy Side**: Customer sells items TO the store (e.g., trade-ins, buybacks)
- **Sales Side**: Customer purchases items FROM the store

This document covers the POS API endpoints for issuing and redeeming coupons, plus real-time Ably integration for receiving configuration updates.

---

## Table of Contents

1. [Authentication](#authentication)
2. [API Endpoints](#api-endpoints)
   - [Get Settings](#get-settings)
   - [Issue Coupon](#issue-coupon)
   - [Validate Coupon](#validate-coupon)
   - [Redeem Coupon](#redeem-coupon)
3. [Ably Real-Time Integration](#ably-real-time-integration)
4. [Error Handling](#error-handling)
5. [Business Rules](#business-rules)
6. [Code Examples](#code-examples)

---

## Authentication

All POS API endpoints require API key authentication via the `X-API-Key` header.

```http
X-API-Key: {store_api_key}
```

| Status | Meaning |
|--------|---------|
| 401 | Missing or invalid API key |

---

## API Endpoints

### Get Settings

Retrieve the current Comeback Cash configuration for both buy and sales sides.

```http
GET /api/{typeNum}/comeback-cash/settings
```

#### Response (200 OK)

```json
{
  "success": true,
  "buy_side": {
    "active": true,
    "event_id": 12,
    "event_name": "Holiday Comeback Cash 2024",
    "earning_type": "flat",
    "earning_tiers": null,
    "earning_flat_amount": 10.00,
    "earning_percentage": null,
    "min_purchase_to_earn": null,
    "redemption_min_purchase": 25.00,
    "allow_double_up": false
  },
  "sales_side": {
    "active": true,
    "event_id": 15,
    "event_name": "Winter Sale Bonus",
    "earning_type": "tiered",
    "earning_tiers": [
      { "min": 50, "max": 99.99, "reward": 5.00 },
      { "min": 100, "max": 199.99, "reward": 15.00 },
      { "min": 200, "max": null, "reward": 30.00 }
    ],
    "earning_flat_amount": null,
    "earning_percentage": null,
    "min_purchase_to_earn": 50.00,
    "redemption_min_purchase": 20.00,
    "allow_double_up": true
  },
  "version": "a1b2c3d4e5f6"
}
```

#### Field Definitions

| Field | Type | Description |
|-------|------|-------------|
| `active` | boolean | Whether an event is currently active for this side |
| `event_id` | int | Unique event identifier |
| `event_name` | string | Display name for receipts/UI |
| `earning_type` | enum | `flat`, `tiered`, or `percentage` |
| `earning_tiers` | array\|null | Tier definitions (for tiered type only) |
| `earning_flat_amount` | decimal\|null | Fixed reward amount (for flat type) |
| `earning_percentage` | decimal\|null | Percentage of transaction (for percentage type) |
| `min_purchase_to_earn` | decimal\|null | Minimum transaction amount to qualify |
| `redemption_min_purchase` | decimal | Minimum purchase required to redeem coupon |
| `allow_double_up` | boolean | Whether coupon can be used WITH other promotions |
| `version` | string | Cache version hash (changes when config updates) |

#### Earning Types

**Flat**: Every qualifying transaction earns the same fixed amount.
```json
"earning_type": "flat",
"earning_flat_amount": 10.00
```

**Tiered**: Reward amount based on transaction amount tiers.
```json
"earning_type": "tiered",
"earning_tiers": [
  { "min": 50, "max": 99.99, "reward": 5.00 },
  { "min": 100, "max": 199.99, "reward": 15.00 },
  { "min": 200, "max": null, "reward": 30.00 }
]
```

**Percentage**: Reward is a percentage of the transaction.
```json
"earning_type": "percentage",
"earning_percentage": 10.00
```

---

### Issue Coupon

Issue a coupon for a qualifying transaction. This endpoint is **idempotent** on `transaction_id`.

```http
POST /api/{typeNum}/comeback-cash/coupons
Content-Type: application/json
```

#### Request Body

```json
{
  "side": "buy",
  "transaction_id": "POS-2024-001234",
  "transaction_amount": 150.00,
  "customer_phone": "+15551234567",
  "customer_name": "John Doe",
  "employee_id": 42
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `side` | string | Yes | `buy` or `sales` |
| `transaction_id` | string | Yes | Unique POS transaction ID (max 50 chars) |
| `transaction_amount` | decimal | Yes | Transaction total (pre-tax merchandise subtotal) |
| `customer_phone` | string | No | Phone for SMS notification (E.164 format preferred) |
| `customer_name` | string | No | Customer name (max 100 chars) |
| `employee_id` | int | No | Employee who processed the transaction |

#### Response (201 Created) - Coupon Issued

```json
{
  "success": true,
  "coupon": {
    "code": "A2B4C6D8",
    "value": 10.00,
    "expires_at": "2025-02-04T23:59:59-06:00",
    "event_name": "Holiday Comeback Cash 2024"
  }
}
```

#### Response (200 OK) - No Qualification

```json
{
  "success": true,
  "coupon": null,
  "reason": "Amount below minimum threshold"
}
```

#### Response (200 OK) - Duplicate Transaction

If the same `transaction_id` is submitted again, returns the existing coupon:

```json
{
  "success": true,
  "coupon": {
    "code": "A2B4C6D8",
    "value": 10.00,
    "expires_at": "2025-02-04T23:59:59-06:00",
    "event_name": "Holiday Comeback Cash 2024"
  }
}
```

---

### Validate Coupon

Check if a coupon code is valid and get its details. Codes are **case-insensitive** and **hyphens are ignored**.

```http
GET /api/{typeNum}/comeback-cash/coupons/{code}
```

#### Response (200 OK) - Valid Coupon

```json
{
  "valid": true,
  "coupon": {
    "code": "A2B4C6D8",
    "value": 10.00,
    "status": "active",
    "expires_at": "2025-02-04T23:59:59-06:00",
    "event_name": "Holiday Comeback Cash 2024",
    "redemption_min_purchase": 25.00
  }
}
```

#### Response (200 OK) - Invalid Coupon

```json
{
  "valid": false,
  "reason": "Coupon has expired",
  "coupon": {
    "code": "A2B4C6D8",
    "value": 10.00,
    "status": "expired",
    "expires_at": "2024-12-31T23:59:59-06:00",
    "event_name": "Holiday Comeback Cash 2024",
    "redemption_min_purchase": 25.00
  }
}
```

#### Invalid Reasons

| Reason | Description |
|--------|-------------|
| `Coupon has expired` | Past expiration date |
| `Coupon already redeemed` | Fully redeemed (value = 0) |
| `Coupon has been voided` | Manually voided by admin |

---

### Redeem Coupon

Process a coupon redemption against a transaction. Supports partial redemptions.

```http
POST /api/{typeNum}/comeback-cash/redeem
Content-Type: application/json
```

#### Request Body

```json
{
  "code": "A2B4-C6D8",
  "transaction_id": "POS-2024-005678",
  "transaction_amount": 75.00,
  "employee_id": 42,
  "redemption_method": "pos",
  "amount_to_redeem": 10.00
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `code` | string | Yes | Coupon code (case-insensitive, hyphens ignored) |
| `transaction_id` | string | Yes | POS transaction ID for audit trail |
| `transaction_amount` | decimal | Yes | Transaction total (pre-tax merchandise subtotal) |
| `employee_id` | int | No | Employee processing redemption (defaults to 0) |
| `redemption_method` | string | No | `pos`, `manual`, or `scan` (defaults to `pos`) |
| `amount_to_redeem` | decimal | No | Partial redemption amount (defaults to full value) |

#### Response (200 OK) - Full Redemption

```json
{
  "success": true,
  "redeemed_amount": 10.00,
  "remaining_value": 0.00,
  "coupon_status": "redeemed"
}
```

#### Response (200 OK) - Partial Redemption

```json
{
  "success": true,
  "redeemed_amount": 5.00,
  "remaining_value": 5.00,
  "coupon_status": "active"
}
```

---

## Ably Real-Time Integration

Subscribe to real-time updates to keep POS configuration in sync without polling.

### Channel

```
{typeNum}
```

Example: `ou00`, `pa00`

### Events

#### `comeback_cash.settings_updated`

Fired when configuration changes (event activated, ended, or settings modified).

```json
{
  "action": "comeback_cash.settings_updated",
  "category": "ou00",
  "timestamp": 1704499200,
  "source": "workspace",
  "version": 1704499200,
  "buy_side": {
    "id": 12,
    "name": "Holiday Comeback Cash 2024",
    "side": "buy",
    "status": "active",
    "isActive": true,
    "earningType": "flat",
    "earningTiers": null,
    "earningFlatAmount": 10.00,
    "earningPercentage": null,
    "minPurchaseToEarn": null,
    "redemptionMinPurchase": 25.00,
    "redemptionDaysValid": 30,
    "allowDoubleUp": false,
    "smsEnabled": true,
    "startDate": "2024-11-01T00:00:00",
    "endDate": "2024-12-31T23:59:59",
    "redemptionStartDate": null,
    "redemptionEndDate": null
  },
  "sales_side": null
}
```

#### `comeback_cash.event_started`

Fired when a new event becomes active.

```json
{
  "action": "comeback_cash.event_started",
  "category": "ou00",
  "timestamp": 1704499200,
  "source": "workspace",
  "event_id": 12,
  "side": "buy",
  "version": 1704499200,
  "buy_side": { /* event config */ },
  "sales_side": null
}
```

#### `comeback_cash.event_ended`

Fired when an event ends or is cancelled.

```json
{
  "action": "comeback_cash.event_ended",
  "category": "ou00",
  "timestamp": 1704499200,
  "source": "workspace",
  "event_id": 12,
  "side": "buy",
  "version": 1704499200,
  "buy_side": null,
  "sales_side": { /* remaining event config if any */ }
}
```

### Integration Pattern

```javascript
// Subscribe to store channel
const channel = ably.channels.get(typeNum);

channel.subscribe('comeback_cash.settings_updated', (message) => {
  // Refresh local configuration cache
  updateLocalConfig(message.data);
});

channel.subscribe('comeback_cash.event_started', (message) => {
  // Enable coupon issuance for this side
  enableCouponIssuance(message.data.side);
});

channel.subscribe('comeback_cash.event_ended', (message) => {
  // Disable coupon issuance for this side
  disableCouponIssuance(message.data.side);
});
```

---

## Error Handling

### HTTP Status Codes

| Status | Meaning |
|--------|---------|
| 200 | Success (including valid=false responses) |
| 201 | Coupon created |
| 400 | Invalid request data |
| 401 | Authentication failed |
| 404 | Resource not found |
| 409 | Conflict (coupon already redeemed) |
| 410 | Gone (coupon expired) |
| 422 | Unprocessable (min purchase not met) |
| 500 | Server error |

### Error Response Format

```json
{
  "success": false,
  "error": "Human-readable error message",
  "code": "ERROR_CODE"
}
```

### Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `UNAUTHORIZED` | 401 | Invalid or missing API key |
| `INVALID_SIDE` | 400 | Side must be 'buy' or 'sales' |
| `INVALID_AMOUNT` | 400 | Negative or non-numeric amount |
| `NO_ACTIVE_EVENT` | 404 | No active event for the specified side |
| `INVALID_CODE` | 404 | Coupon code not found |
| `ALREADY_REDEEMED` | 409 | Coupon has been fully redeemed |
| `EXPIRED` | 410 | Coupon has expired |
| `MIN_PURCHASE_NOT_MET` | 422 | Transaction below minimum purchase |

### Minimum Purchase Error

The `MIN_PURCHASE_NOT_MET` error includes the required amount:

```json
{
  "success": false,
  "error": "Transaction amount does not meet minimum purchase requirement",
  "code": "MIN_PURCHASE_NOT_MET",
  "minimum_required": 25.00
}
```

---

## Business Rules

### Coupon Issuance

1. **One active event per side**: Only one buy-side and one sales-side event can be active simultaneously
2. **Buy-side uses flat earning only**: Buy-side events always issue a fixed reward amount
3. **Transaction amount is pre-tax**: Use merchandise subtotal, not including taxes
4. **Sales-side uses net amount**: Apply discounts before checking thresholds
5. **Idempotent on transaction_id**: Duplicate requests return existing coupon without re-issuing

### Coupon Codes

- 8 uppercase alphanumeric characters
- Excludes ambiguous characters: `0`, `O`, `I`, `L`, `1`
- Case-insensitive lookup
- Hyphens ignored (display as `XXXX-XXXX`, accept either format)

### Redemption

1. **Bearer instruments**: No customer identity verification required
2. **Minimum purchase required**: Transaction must meet `redemption_min_purchase`
3. **Partial redemption supported**: Use `amount_to_redeem` for less than full value
4. **Coupon remains active**: Until remaining value < $0.01
5. **Expiration enforced**: Cannot redeem after `expires_at`

### SMS Notifications

- Sent when: phone provided AND event has SMS enabled
- Contains: code, value, expiration, event name
- Format: E.164 preferred (`+15551234567`)
- Accepted: 10+ digit formats (auto-formatted to E.164)

---

## Code Examples

### JavaScript/Node.js

```javascript
const axios = require('axios');

const api = axios.create({
  baseURL: 'https://app.buyerkiosk.com/api/ou00/comeback-cash',
  headers: { 'X-API-Key': 'your-api-key' }
});

// Get settings
async function getSettings() {
  const { data } = await api.get('/settings');
  return data;
}

// Issue coupon
async function issueCoupon(side, transactionId, amount, phone = null) {
  const { data } = await api.post('/coupons', {
    side,
    transaction_id: transactionId,
    transaction_amount: amount,
    customer_phone: phone
  });
  return data;
}

// Validate coupon
async function validateCoupon(code) {
  const { data } = await api.get(`/coupons/${code}`);
  return data;
}

// Redeem coupon
async function redeemCoupon(code, transactionId, amount) {
  const { data } = await api.post('/redeem', {
    code,
    transaction_id: transactionId,
    transaction_amount: amount,
    redemption_method: 'pos'
  });
  return data;
}
```

### cURL

```bash
# Get settings
curl -X GET \
  'https://app.buyerkiosk.com/api/ou00/comeback-cash/settings' \
  -H 'X-API-Key: your-api-key'

# Issue coupon
curl -X POST \
  'https://app.buyerkiosk.com/api/ou00/comeback-cash/coupons' \
  -H 'X-API-Key: your-api-key' \
  -H 'Content-Type: application/json' \
  -d '{
    "side": "buy",
    "transaction_id": "POS-001234",
    "transaction_amount": 150.00,
    "customer_phone": "+15551234567"
  }'

# Validate coupon
curl -X GET \
  'https://app.buyerkiosk.com/api/ou00/comeback-cash/coupons/A2B4C6D8' \
  -H 'X-API-Key: your-api-key'

# Redeem coupon
curl -X POST \
  'https://app.buyerkiosk.com/api/ou00/comeback-cash/redeem' \
  -H 'X-API-Key: your-api-key' \
  -H 'Content-Type: application/json' \
  -d '{
    "code": "A2B4C6D8",
    "transaction_id": "POS-005678",
    "transaction_amount": 75.00,
    "redemption_method": "pos"
  }'
```

---

## Testing

### Sandbox Environment

Use `https://dev2.buyerkiosk.com` for testing with test store credentials.

### Test Flow

1. Call `GET /settings` to verify active events
2. Issue a test coupon with `POST /coupons`
3. Validate the coupon with `GET /coupons/{code}`
4. Redeem the coupon with `POST /redeem`
5. Verify coupon status is now `redeemed`

---

## Support

For API key provisioning or integration support, contact your BuyerKiosk account representative.
