> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spurtcommerce.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Coupon

The SpurtCommerce Coupon Add-on adds coupon functionality to an existing SpurtCommerce API without requiring you to copy the coupon module into the core application.

Install the npm package, register its database entities and migrations, initialize the module, and expose its controllers through the API server. Then connect the coupon processor to the order-creation flow so a coupon validated in the storefront is applied when the customer checks out.

> **Documentation note:** This guide is based on the published `spurtcommerce-coupon` package README and SpurtCommerce v6 release information. Some integration points depend on the exact checkout implementation in your project. Where the package README does not define a request shape or function signature, this guide identifies what you must confirm in the package source instead of inventing values.

## What you will build

After completing this guide, your SpurtCommerce API will expose three groups of coupon endpoints:

* **Admin:** manage coupons, inspect usage, and export coupon reports.
* **Vendor:** manage vendor-specific coupons and their active status.
* **Storefront:** validate a coupon against the customer's cart and return the discount data needed by checkout.

The add-on also exports a checkout processor that calculates and persists coupon discounts and coupon usage.

### How the integration fits together

```mermaid theme={null}
flowchart TD
    A[Admin or Vendor creates a coupon] --> B[Coupon data stored in database]
    C[Customer enters coupon code] --> D[POST /api/customer-coupon/apply-coupon]
    B --> D
    D --> E[Coupon eligibility and cart validation]
    E --> F[Encrypted applied-products payload]
    F --> G[Customer proceeds to checkout]
    G --> H[SpurtCommerce order-creation flow]
    H --> I[processCoupon]
    I --> J[Order item discounts and coupon usage]
    J --> K[Order saved]
```

The storefront validation endpoint and the checkout processor have different responsibilities:

1. `apply-coupon` checks whether a coupon can be applied to the submitted cart and returns the discount information.
2. `process()` is called during order creation to calculate and persist the discount against the order.

Do not treat a successful storefront response as a substitute for checkout processing.

## Before you begin

### Compatibility requirements

The package README lists the following requirements:

| Component | Required version |
| - | - |
| Node.js | 18.x or later |
| TypeScript | 4.9.x or 5.x |
| TypeORM | 0.3.x or later |
| `routing-controllers` | 0.11.x or later |
| `typedi` | 0.10.x or later |
| SpurtCommerce | Community or Enterprise |

SpurtCommerce v6.0.0 release notes specify Node.js 22.23.1 and TypeORM 0.3.26, so that release meets the package's stated TypeORM requirement. Check your own project's installed versions before proceeding.

Run these commands from the SpurtCommerce API project root:

```bash theme={null}
node --version
npm --version
npm ls typeorm routing-controllers typedi
```

If your project uses TypeORM 0.2.x, stop before integrating this package. The package requires TypeORM 0.3.x or later and uses the `DataSource` API. Do not attempt to resolve this by changing only the package version in `package.json`; upgrade and test the application's TypeORM integration as a separate compatibility task.

### Back up and use a non-production environment

Before adding the module:

1. Commit or back up your current application changes.
2. Take a database backup.
3. Perform the integration in a development or staging environment.
4. Review the package migrations before running them against an existing database.
5. Confirm the coupon endpoints do not conflict with routes already registered by your application.

### Package and source links

* [Installable npm package: `spurtcommerce-coupon`](https://www.npmjs.com/package/spurtcommerce-coupon)
* [SpurtCommerce source repository](https://github.com/spurtcommerce/spurtcommerce)
* [SpurtCommerce v6.0.0 release notes](https://github.com/spurtcommerce/spurtcommerce/releases)

## 1. Install the package

Open a terminal in the SpurtCommerce API project root — the directory containing the API's `package.json`.

```bash theme={null}
npm install spurtcommerce-coupon
```

Confirm that npm installed the package:

```bash theme={null}
npm ls spurtcommerce-coupon
```

For local development where the add-on package is available in your repository under `add-ons/Coupon`, the package README also supports a local path installation:

```bash theme={null}
npm install ./add-ons/Coupon
```

Use the npm installation for normal application integration. Use the local path when you are actively developing or testing the add-on package itself.

## 2. Register the coupon database entities

The add-on stores coupon definitions, product/category restrictions, and redemption records in the application's database. TypeORM must know about the add-on entities before the application initializes its `DataSource`.

Open the TypeORM configuration used by your API. The package README gives `src/loaders/typeormLoader.ts` as an example location.

Import the entities:

```typescript theme={null}
import { CouponEntities } from 'spurtcommerce-coupon';
```

Append them to the existing `entities` array:

```typescript theme={null}
const dbConfig: DataSourceOptions = {
    // Keep the rest of your existing database configuration.
    entities: [
        ...Object.values(entities).filter(
            (entity) => typeof entity === 'function'
        ),
        ...CouponEntities,
    ],
};
```

Keep your existing entities. The important change is adding `...CouponEntities` to the array; do not replace the application's entity list with only the coupon entities.

### Tables provided by the add-on

| Entity | Table | What it stores |
| - | - | - |
| `VendorCoupon` | `vendor_coupon` | Coupon configuration and details. |
| `VendorCouponProductCategory` | `coupon_product_category` | Product and category eligibility mappings. |
| `CouponUsage` | `coupon_usage` | Coupon redemption/usage records. |
| `CouponUsageProduct` | `coupon_usage_product` | Product-level discount allocations for a redemption. |

The package also exports `BaseModel`, a base entity with timestamp audit columns.

## 3. Register the coupon migrations

Import the migration collection:

```typescript theme={null}
import { CouponMigrations } from 'spurtcommerce-coupon';
```

Append it to the migrations array in the same TypeORM configuration:

```typescript theme={null}
const dbConfig: DataSourceOptions = {
    // Keep the rest of your existing database configuration.
    migrations: [
        ...Object.values(migrations),
        ...CouponMigrations,
    ],
};
```

Do not create a second database configuration just for the add-on. Register its migrations in the DataSource used by the SpurtCommerce API.

### Migrations included in the package

| Migration | Purpose |
| - | - |
| `CreateCouponTable1679032959092` | Creates the `vendor_coupon` table. |
| `CreateCouponUsageTable1679033604526` | Creates the `coupon_usage` table. |
| `CreateCouponUsageProductTable1679033767307` | Creates the `coupon_usage_product` table. |
| `CreateCouponProductCategoryTable1679034454853` | Creates the `coupon_product_category` table. |
| `AddCouponInPluginTable1680073626615` | Inserts coupon plugin metadata into the `plugins` table. |
| `UpdatePluginStatusCoupon1759829955170` | Updates coupon plugin routes and status in the `plugins` table. |

### Run and verify the migrations

Use the migration command configured by your SpurtCommerce project's `package.json`. The command can differ between project setups, so use the script defined by your checked-out version rather than copying a generic TypeORM command.

After the migration completes, verify that the four coupon tables exist and inspect the `plugins` table for the coupon metadata changes. If the migration fails, stop and resolve the schema or migration-history issue before starting the API with the add-on enabled.

## 4. Initialize the coupon module

The package must receive the same TypeORM `DataSource` that the host application uses.

Import `initCoupon`:

```typescript theme={null}
import { initCoupon } from 'spurtcommerce-coupon';
```

Call it after the database connection has initialized successfully. The package README uses this pattern:

```typescript theme={null}
initCoupon({
    dataSource,
});
```

For example, if your loader exposes a `getDataSource()` helper, the setup can be organized as follows:

```typescript theme={null}
import { initCoupon } from 'spurtcommerce-coupon';
import { getDataSource } from './typeormLoader';

export async function setupCouponModule(): Promise<void> {
    const dataSource = getDataSource();

    initCoupon({
        dataSource,
    });
}
```

Call `setupCouponModule()` only after `dataSource.initialize()` has completed. Adapt the example to your actual loader lifecycle; do not initialize a second DataSource for the add-on.

### Optional host adapters

The add-on supports adapters so it can work with the host application's product, customer, order, logging, and vendor-middleware implementations.

```typescript theme={null}
initCoupon({
    dataSource,

    // Optional: connect to the host product service.
    // productAdapter: {
    //     findOne: async (options) => productService.findOne(options),
    //     getProductDetail: async (id) => productService.getProductDetail(id),
    // },

    // Optional: connect to the host customer service.
    // customerAdapter: {
    //     findOne: async (options) => customerService.findOne(options),
    // },

    // Optional: connect coupon discount updates to order items.
    // orderAdapter: {
    //     updateOrderProductDiscount: async (params) => {
    //         // Implement using your application's order-product service.
    //     },
    // },

    // Optional logger. Defaults to console.
    // logger: console,

    // Optional vendor route middleware.
    // vendorMiddleware: CheckVendorAddonMiddleware,
});
```

The adapter methods shown above are examples from the package README. Use the actual exported TypeScript interfaces to confirm parameter and return types before implementing a custom adapter.

Use a custom adapter when the default adapter cannot correctly access your host application's product, customer, or order data. In particular, test product lookup and order-item discount updates with your own entity structure.

## 5. Register the coupon controllers

The add-on's controllers must be included when the Express server is created.

In the API's Express loader — the package README gives `src/loaders/expressLoader.ts` as an example — import the controller collection:

```typescript theme={null}
import { CouponControllers } from 'spurtcommerce-coupon';
```

Append the controllers to the existing controller array:

```typescript theme={null}
createExpressServer({
    controllers: [
        ...controllers,
        ...CouponControllers,
    ],
});
```

Alternatively, the package exports a helper:

```typescript theme={null}
import { registerCouponControllers } from 'spurtcommerce-coupon';

registerCouponControllers(controllersList);
```

Use one approach only. Do not add the same coupon controllers through both methods, because duplicate registration can create confusing route behavior.

Restart the API after registration. The route tables in the API reference below should then be available under the application's configured API base URL.

## 6. Understand the coupon application flow

A typical storefront integration has two distinct stages.

### Stage A — Validate the coupon against the cart

When a customer enters a coupon code, the storefront sends the coupon code and the current cart product details to:

```http theme={null}
POST /api/customer-coupon/apply-coupon
Content-Type: application/json
```

Example request body:

```json theme={null}
{
    "couponCode": "SUMMER20",
    "emailId": "customer@example.com",
    "productDetail": [
        {
            "productId": 1,
            "skuName": "SHIRT-RED-M",
            "productPrice": 1000,
            "quantity": 2,
            "total": 2000
        }
    ]
}
```

In this example, the cart contains two units of a product priced at 1,000 each. The example uses a coupon named `SUMMER20`; it does not imply that this coupon exists in your database.

The API checks coupon eligibility and calculates the applicable discount. The package README identifies checks for:

* Percentage or fixed-amount discounts.
* Minimum and maximum order amounts.
* Restricted customer email addresses.
* Product and category eligibility.

Example successful response:

```json theme={null}
{
    "status": 1,
    "message": "Successfully added product to cart",
    "data": {
        "vendorCouponId": 1,
        "couponCode": "SUMMER20",
        "grandDiscountAmount": 400,
        "appliedProducts": "<encrypted-payload>"
    }
}
```

The `grandDiscountAmount` in this example is illustrative. The actual amount is determined by the coupon configuration and eligible cart products.

### What the storefront should do with the response

When the API accepts the coupon:

1. Read the returned coupon information.
2. Display the returned discount amount in the cart summary.
3. Retain the encrypted `appliedProducts` value for the checkout flow required by your application.
4. Recalculate or revalidate when the cart changes.
5. Do not let the browser calculate the authoritative discount independently.

Treat `appliedProducts` as an opaque encrypted payload. Do not decode, edit, or recreate it in storefront code.

The package README does not specify the complete storefront state model or the exact checkout request property used to carry this payload. Confirm the expected property from the package implementation and your checkout controller before wiring the frontend.

### Stage B — Process the coupon during checkout

Coupon validation is not the final order operation. During order creation, the API must call the add-on's exported `process()` function so the coupon discount is calculated and recorded against the order.

Import the function with an alias to avoid confusion with other functions named `process`:

```typescript theme={null}
import { process as processCoupon } from 'spurtcommerce-coupon';
```

The package README documents this call shape:

```typescript theme={null}
const couponResult = await processCoupon(
    couponData,   // { couponCode, couponData, couponDiscountAmount }
    orderData,    // { orderId, customerId }
    dynamicData,  // { [skuName]: { price } }
    totalAmount   // Order gross total
);
```

The arguments represent:

| Argument | Expected information |
| - | - |
| `couponData` | Coupon information: `couponCode`, `couponData`, and `couponDiscountAmount`. |
| `orderData` | The created order's `orderId` and `customerId`. |
| `dynamicData` | Product pricing indexed by SKU name. |
| `totalAmount` | Gross order total before coupon discount. |

The function returns:

```typescript theme={null}
{
    total: number;
    couponCode: string;
    discountAmount: number;
}
```

* `total` is the net total after the coupon discount.
* `couponCode` identifies the coupon processed.
* `discountAmount` is the total discount applied.

### Where to call `processCoupon()`

The package README identifies the customer order controller as the integration point:

`src/api/store/controllers/CustomerOrderController.ts`

Place the call inside the existing order-creation flow, where the application has the order ID, customer ID, cart/SKU pricing data, gross total, and coupon data available.

The following is an integration outline, not a drop-in replacement for your controller:

```typescript theme={null}
// 1. Complete the normal order validation.
// 2. Prepare the order and the values required by processCoupon().
// 3. Call processCoupon() as part of the order-creation workflow.
// 4. Use the returned total and discount in the order calculation.
// 5. Complete order persistence using the application's existing flow.

const couponResult = await processCoupon(
    couponData,
    {
        orderId: orderData.orderId,
        customerId: orderData.customerId,
    },
    dynamicData,
    totalAmount
);

// Use the result in the order totals according to your
// existing order calculation and persistence implementation.
```

**Important integration detail:** The README gives the shape of `couponData`, but does not document the exact mapping from the storefront's `appliedProducts` response to that argument. Inspect the package's `process()` implementation and the checkout controller to confirm how the encrypted payload is passed and decrypted. Do not assume that the entire `apply-coupon` response can be passed directly as `couponData`.

### Keep order totals consistent

When integrating the processor, verify that:

* The gross total passed to the processor is the intended pre-discount order amount.
* `dynamicData` contains the SKU names and prices expected by the package.
* The returned `total` is used consistently in the order's final amount.
* Product-level discounts are reflected in the appropriate order items.
* Coupon usage is recorded only for the successfully created order.
* A failed order does not leave inconsistent coupon usage or discount records.

The package provides an `OrderAdapter` for updating product-level order discounts. If your order item structure differs from the package's default expectations, implement and test a custom adapter.

## 7. Cart changes and coupon revalidation

A coupon can become invalid after it has been applied. For example, a customer may remove an eligible product, change a quantity, or reduce the cart below the coupon's minimum order amount.

The package README identifies a procedural function named `CouponProccess()` for removing invalid coupon product-category entries when products change.

Before calling it, inspect the exported function signature in the installed package to confirm its parameters and expected return behavior. The README does not provide a call signature, so a safe copy-and-paste invocation cannot be supplied here.

At the storefront level, revalidate the coupon whenever a cart change could affect eligibility. Do not continue displaying a previously calculated discount after the cart contents have changed unless the coupon has been validated for the updated cart.

## 8. API reference

All routes below are supplied by the package README. The application must register `CouponControllers` (or use `registerCouponControllers()`) before these routes are available.

Replace `<API_BASE_URL>` with the base URL of your SpurtCommerce API.

### Admin coupon APIs

Controller: `AdminCouponController`\
Base path: `/api/admin-coupon`

| Method | Endpoint | Description |
| - | - | - |
| `POST` | `/api/admin-coupon/add-coupon` | Create a coupon. |
| `GET` | `/api/admin-coupon/admin-coupon-list` | List coupons with pagination, keyword, status, and date filters. |
| `GET` | `/api/admin-coupon/coupon-usage-list` | View coupon usage history and customer redemptions. |
| `GET` | `/api/admin-coupon/coupon-detail` | Retrieve coupon details by `couponId`. |
| `PUT` | `/api/admin-coupon/update-coupon/:vendorCouponId` | Update a coupon. |
| `DELETE` | `/api/admin-coupon/delete-coupon/:vendorCouponId` | Delete a coupon. |
| `POST` | `/api/admin-coupon/delete-bulk-coupon` | Delete multiple coupons by IDs. |
| `GET` | `/api/admin-coupon/bulk-export-admin-coupon-list` | Export the coupon list to CSV or Excel. |
| `GET` | `/api/admin-coupon/export-excel-admin-coupon-list` | Export selected coupons to Excel. |
| `GET` | `/api/admin-coupon/remove-coupon` | Remove a coupon. |

The package README does not include the query parameters, request DTOs, or response schemas for these routes. Use the controller DTOs and route implementation in the installed package as the source of truth when preparing API calls.

### Vendor coupon APIs

Controller: `VendorCouponController`\
Base path: `/api/vendor-coupon`

| Method | Endpoint | Description |
| - | - | - |
| `POST` | `/api/vendor-coupon/add-coupon` | Create a vendor coupon. |
| `GET` | `/api/vendor-coupon/vendor-coupon-list` | List vendor coupons with search, status, and date filters. |
| `GET` | `/api/vendor-coupon/coupon-usage-list` | View vendor coupon usage. |
| `GET` | `/api/vendor-coupon/vendor-coupon-detail` | Retrieve vendor coupon details by ID. |
| `PUT` | `/api/vendor-coupon/update-vendor-coupon/:vendorCouponId` | Update a vendor coupon. |
| `PUT` | `/api/vendor-coupon/status-update` | Activate or deactivate a vendor coupon. |
| `DELETE` | `/api/vendor-coupon/delete-vendor-coupon/:vendorCouponId` | Delete a vendor coupon. |

Vendor routes must use the appropriate vendor authorization and middleware configuration for your installation. The package supports an optional `vendorMiddleware` during initialization; verify that it is connected to the correct host middleware.

### Storefront customer coupon API

Controller: `CustomerCouponController`\
Base path: `/api/customer-coupon`

| Method | Endpoint | Description |
| - | - | - |
| `POST` | `/api/customer-coupon/apply-coupon` | Validate a coupon against cart products and return discount information with an encrypted applied-products payload. |

This is the storefront endpoint documented in the package README. The README does not list separate customer endpoints for removing or listing applied coupons.

## 9. Services and repositories

The package exports the following services and repositories for its internal coupon operations.

### Repositories

| Export | Responsibility |
| - | - |
| `VendorCouponRepository` | Query active coupons and validate purchase amounts. |
| `VendorCouponProductCategoryRepository` | Manage coupon product/category assignments. |
| `CouponUsageRepository` | Track coupon redemption records. |
| `CouponUsageProductRepository` | Track product-level coupon discount allocations. |

### Services

| Export | Responsibility |
| - | - |
| `VendorCouponService` | Vendor coupon business operations. |
| `VendorCouponProductCategoryService` | Coupon category-mapping operations. |
| `CouponUsageService` | Coupon redemption history. |
| `CouponUsageProductService` | Product-level coupon usage records. |

Most integrations should use the documented module initialization, controllers, and checkout processor rather than directly manipulating repositories. Use the service and repository exports only when you have a specific extension requirement and have checked their public method signatures.

## 10. Verify the integration

Complete these checks in a development or staging environment before releasing the add-on.

### Installation and startup

* [ ] `npm ls spurtcommerce-coupon` shows the installed package.
* [ ] The API starts without TypeORM metadata or dependency-resolution errors.
* [ ] The coupon entities are registered with the application's DataSource.
* [ ] The coupon migrations complete successfully.
* [ ] The coupon controllers are registered once.

### Admin and Vendor

* [ ] An authorized Admin can create and list a coupon.
* [ ] An authorized Admin can update and delete a coupon.
* [ ] An authorized Vendor can manage their own coupon.
* [ ] Vendor authorization prevents access to another vendor's coupon data.
* [ ] Coupon usage history and exports return the expected records.

### Storefront and checkout

* [ ] A valid coupon is accepted for an eligible cart.
* [ ] An invalid or expired coupon is rejected.
* [ ] Minimum and maximum order amount rules are enforced.
* [ ] Restricted customer email rules are enforced.
* [ ] Product/category restrictions are enforced.
* [ ] The storefront treats the encrypted payload as opaque data.
* [ ] Changing the cart causes coupon eligibility to be checked again.
* [ ] Checkout applies the expected order-level and product-level discounts.
* [ ] Coupon usage records match successful orders.
* [ ] A failed checkout does not leave incorrect order or coupon usage data.

## 11. Troubleshooting

| Symptom | Checks |
| - | - |
| TypeScript reports that `DataSource` or a TypeORM export is missing | Confirm that the host application uses TypeORM 0.3.x or later. The package does not support a TypeORM 0.2 integration according to its stated requirements. |
| Coupon entity metadata is missing | Confirm `CouponEntities` is appended to the entities array used by the active DataSource. |
| Coupon tables do not exist | Confirm `CouponMigrations` is registered and the project's migration command completed successfully. |
| Coupon routes return 404 | Confirm `CouponControllers` is included in `createExpressServer()` and the API has restarted. |
| Coupon routes are registered more than once | Use either the controller array or `registerCouponControllers()`, not both. |
| Product eligibility checks fail | Check the product adapter and verify that product IDs, SKU names, and category data match the host application's data. |
| Customer validation fails unexpectedly | Check coupon status, validity, order amount, restricted emails, and product/category restrictions. |
| Vendor requests fail authorization | Check the configured vendor middleware and the host application's vendor authorization behavior. |
| Checkout discount does not match the storefront | Check the `couponData` mapping, encrypted payload handling, SKU keys in `dynamicData`, gross total, and custom order adapter. |
| Coupon usage and order totals disagree | Review the order transaction and error-handling flow to ensure order persistence and coupon processing remain consistent. |

## 12. Production deployment considerations

Before enabling the add-on in production:

1. Test the complete Admin → Storefront → Checkout flow in staging.
2. Review the migrations and take a verified database backup.
3. Confirm that Admin and Vendor routes enforce the intended authorization.
4. Keep the encrypted applied-products payload unchanged between validation and checkout.
5. Ensure checkout recalculates and persists discounts on the server; never trust a client-supplied discount amount as authoritative.
6. Test failed checkout and retry scenarios for duplicate or inconsistent coupon usage.
7. Monitor API errors and database migration results after deployment.

## 13. Package development

These commands are for developers modifying the coupon add-on package itself, rather than applications that simply install it.

Run them from the add-on package directory.

Build the package:

```bash theme={null}
npm run build
```

Run the development script:

```bash theme={null}
npm run dev
```

Check the files that would be included in the npm package:

```bash theme={null}
npm pack --dry-run
```

## Related resources

* [spurtcommerce-coupon on npm](https://www.npmjs.com/package/spurtcommerce-coupon)
* [SpurtCommerce GitHub repository](https://github.com/spurtcommerce/spurtcommerce)
* [SpurtCommerce releases](https://github.com/spurtcommerce/spurtcommerce/releases)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.