---
updatedAt: 2026-04-10T18:48:49.000Z
agentTools:
  siteIndex: https://docs.affirm.com/llms.txt
  projectIndex: https://docs.affirm.com/platforms/llms.txt
---

# Controllers & Pipelines cartridge installation

Explore Controllers and Pipelines cartridge installation in Salesforce Commerce Cloud (SFCC).

<HTMLBlock>{`
<style>
  .dropdown-menu {
    font-family: Arial, sans-serif;
    max-width: 250px;
    margin-left: auto; /* Align to the right */
  	margin-right: 0; /* Ensure it's flush to the right */
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 10px;
    background: #4A4AF4;
    overflow: hidden; /* Ensure the container grows with content */
  }

  .dropdown-item {
    margin-bottom: 10px;
  }

  .dropdown-toggle {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px;
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 4px;
    cursor: pointer;
  }

  .dropdown-toggle:hover {
    background: #f0f0f0;
  }

  .dropdown-content {
    display: none;
    margin-top: 5px;
    padding: 10px;
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 4px;
  }

  .dropdown-item.open .dropdown-content {
    display: block;
  }

  .flag-icon {
    width: 20px;
    height: 20px;
    margin-right: 10px;
  }

  .product-item {
    margin-bottom: 5px;
  }

  .product-icon {
    width: 20px;
    height: 20px;
    margin-right: 10px;
  }
  
  .country-title {
    font-family: Arial, sans-serif;
    font-size:20px;
    text-align: center;
    font-weight: bold;
    color: #ffffff;
  } 

</style>
  <div class="dropdown-menu">
    <p class="country-title" alt="Countries where this product is available">Country Availability</p>

    <div class="dropdown-item">
      <button class="dropdown-toggle" onclick="toggleDropdown(event)" alt="Drop down menu displaying the available countries for this product">
        <b>Country List</b> ⬇️
      </button>
      <div class="dropdown-content">
        <div class="product-item">
          <img src="https://files.readme.io/a4c8b3d6596d943e2b93c82cb1d432a2edaf6998667835c2e97ef4a31d460c7e-us-circle-01.png" alt="USA" class="flag-icon">

          USA
        </div>
        <div class="product-item">
          <img src="https://files.readme.io/3ca294a1b4b8b8506c3ff32876abc6e3e33d0e65a509dab81398915094bdbc5c-61TcZ33ZrJL._AC_UY1000_.jpg" alt="Canada" class="flag-icon">

          Canada
        </div>
      </div>
    </div>

  </div>
`}</HTMLBlock>

## Overview

This page describes changes that should be made to the site for SiteGenesis storefronts. There are four types of custom modifications to your core cartridge:

* [Templates](https://docs.affirm.com/platforms/docs/sfcc-controllers-pipelines#templates)
* [Client-side JavaScript](https://docs.affirm.com/platforms/docs/sfcc-controllers-pipelines#client-side-javascript)
* [Controllers (SiteGenesis Controllers version)](https://docs.affirm.com/platforms/docs/sfcc-controllers-pipelines#controllers-sitegenesis-controllers)
* [Pipelines (SiteGenesis Pipelines version)](https://docs.affirm.com/platforms/docs/sfcc-controllers-pipelines#pipelines-sitegenesis-pipelines)

***

## Templates

### 1. cartridge/templates/default/components/header/htmlhead.isml

Copy the following code and and paste it after the Google verification feature:

```html htmlhead.isml
<isinclude template="affirm/affirmheader" />
```

<Image align="center" alt="screenshot of the code" border={true} width="100%" src="https://files.readme.io/aa94a8b-cp_1_htmlhead.png" title="cp_1_htmlhead.png" className="border" />

### 2. cartridge/templates/default/components/footer/footer\_UI.isml

Copy and paste the following code after the last line:

```html footer_UI.isml
<isinclude template="affirm/affirmfooter" />
```

![screenshot of the code](https://files.readme.io/731fe82-cp_2_footer_UI.png "cp_2_footer_UI.png")

### 3. cartridge/templates/default/checkout/cart/cart.isml

Find the closing tag for the div with `cart-footer` class (line 850). Copy and paste the following code right before the closing `</fieldset>` tag:

```html cart.isml
<isaffirmpromo context="cart" fpname="${require('int_affirm/cartridge/scripts/utils/affirmUtils').getFPNameByBasket(pdict.Basket)}">
```

<Image align="center" alt="screenshot of the cart code" width="smart" src="https://files.readme.io/eee04f7-cp_3_cart.png" title="cp_3_cart.png" />

### 4. cartridge/templates/default/product/components/options.isml

Add the following `data-affirm-name` property to the `select` component:

```html options.isml
<select id="${Option.htmlName}" name="${Option.htmlName}" data-affirm-name="${Option.ID}" class="product-option input-select">
```

![screenshot of the code](https://files.readme.io/2898719-cp_4_options.png "cp_4_options.png")

### 5. cartridge/templates/default/product/productcontent.isml

Copy and paste the following code after `pricing component`:

```html productcontent.isml
<isinclude template="util/affirmmodule"/>
<isaffirmpromo context="pdp" fpname="${require('int_affirm/cartridge/scripts/utils/affirmUtils').getFinancingProgramByProduct(pdict.Product, true)}" />
```

![screenshot of the code](https://files.readme.io/7249b7e-cp_5_productcontent.png "cp_5_productcontent.png")

### 6. cartridge/templates/default/checkout/billing/paymentmethods.isml

Locate the following code:

![div class = form-row label-inline](https://files.readme.io/6b05cec-cp_6_paymentmethods-1.png "cp_6_paymentmethods-1.png")

Then, replace it with the following code:

![isif condition = ](https://files.readme.io/ee237f1-cp_6_paymentmethods-2.png "cp_6_paymentmethods-2.png")

Insert the following tag after the Custom processor comment tag:

```html paymentmethods.isml
<isinclude template="affirm/affirmpaymentmethod" />
```

![screenshot of code](https://files.readme.io/cff0e2e-cp_6_paymentmethods-3.png "cp_6_paymentmethods-3.png")

### 7. cartridge/templates/default/checkout/summary/summary.isml

Copy and paste the following code after the `COSummary-Submit` form:

```html summary.isml
<isinclude template="affirm/vcndata" />
```

![screenshot of code](https://files.readme.io/60d0716-cp_7_summary.png "cp_7_summary.png")

### 8. cartridge/templates/default/product/producttile.isml

Copy and paste the following code below after the `product swatches` block:

```html producttile.isml
<isinclude template="util/affirmmodule"/>
<isaffirmpromo context="plp" fpname="${require('int_affirm/cartridge/scripts/utils/affirmUtils').getFPNameForPLP(pdict.CurrentHttpParameterMap.cgid.value, Product)}" price="${prices}"/>
```

![](https://files.readme.io/94dad46-cp_8_producttile.png "cp_8_producttile.png")

### 9. cartridge/templates/default/product/producttopcontentPS.isml

Copy and paste the following code after the div with `product-number` class:

```html producttopcontentPS.isml
<isinclude template="util/affirmmodule"/>
<isaffirmpromo context="pdp" fpname="${require('int_affirm/cartridge/scripts/utils/affirmUtils').getFinancingProgramByProduct(pdict.Product, true)}" />
```

![](https://files.readme.io/9a04331-cp_9_producttopcontentPS.png "cp_9_producttopcontentPS.png")

### 10. cartridge/templates/default/util/modules.isml

Copy and paste the following code after the content asset module import:

```html modules.isml
<isinclude template="util/affirmmodule"/>
```

![](https://files.readme.io/80957ea-cp_10_modules.png "cp_10_modules.png")

### 11. cartridge/templates/default/components/order/orderdetails.isml

Replace the div with `payment-type` class and `isminicreditcard` tag with the following code:

```html orderdetails.isml
<isif condition="${!paymentInstr.custom.affirmed}">
			<div class="payment-type"><isprint value="${dw.order.PaymentMgr.getPaymentMethod(paymentInstr.paymentMethod).name}" /> </div>
			<isminicreditcard card="${paymentInstr}" show_expiration="${false}"/>
		<iselse/>
			<isaffirmpaymenttype email="${false}"/>
</isif>
```

![](https://files.readme.io/fc107e0-cp_11_orderdetails.png "cp_11_orderdetails.png")

### 12. cartridge/templates/default/components/order/orderdetailsemail.isml

Replace lines above `isminicreditcard` tag (lines 46-50) with the following code:

```html orderdetailsemail.isml
<isif condition="${!paymentInstr.custom.affirmed}">
	<div><isprint value="${dw.order.PaymentMgr.getPaymentMethod(paymentInstr.paymentMethod).name}" /></div>
	<isif condition="${dw.order.PaymentInstrument.METHOD_GIFT_CERTIFICATE.equals(paymentInstr.paymentMethod)}">
		<isprint value="${paymentInstr.maskedGiftCertificateCode}"/><br />
	</isif>
	<isminicreditcard card="${paymentInstr}" show_expiration="${false}"/>
<iselse/>
	<isaffirmpaymenttype email="${true}"/>
</isif>
```

![](https://files.readme.io/1c12aec-cp_12_orderdetailsemail.png "cp_12_orderdetailsemail.png")

### 13. cartridge/templates/default/checkout/confirmation/confirmation.isml

Below the close tag of the div with `confirmation message` class, copy and paste the following code to include the tracking script template:

```html confirmation.isml
<isinclude url="${URLUtils.http('Affirm-Tracking', 'orderId', pdict.Order.orderNo)}" >
```

![](https://files.readme.io/5308771-cp_13_confirmation.png "cp_13_confirmation.png")

### 14. int\_affirm\_controllers/cartridge/templates/resources/affirm.properties

Find the property `affirm.controllers.cartridge` and set it as the name of your controllers' cartridge with script files `app.js`, `guard.js`. Example:

```text affirm.properties
affirm.controllers.cartridge=app_storefront_controllers
```

***

## Client-side JavaScript

### 1. cartridge/js/pages/product/variant.js

Update the `ajax.load` within the `updateContent` method with the following code snippet:

```javascript variant.js
if (typeof affirm !== "undefined"){
    affirm.ui.refresh();
}
```

![](https://files.readme.io/4c25c4d-cp_js_1_variant.png "cp_js_1_variant.png")

### 2. cartridge/js/pages/search.js

In the `$('#man').load` jQuery callback, include the following code snippet:

```javascript search.js
if (typeof affirm !== "undefined"){
    affirm.ui.refresh();
}
```

![](https://files.readme.io/aa7df5d-cp_js_2_search.png "cp_js_2_search.png")

### 3. cartridge/js/pages/product/productSet.js

Update `on('click')` of event action with `$productSetList` with the following extended code:

```javascript productSet.js
var updateAffirmItems = function($container, oldSKU) {
        var affirmItem = affirmItems.find(function (item) {
            return item.sku === oldSKU;
        });
 
        affirmItem.sku = $container.find('input[name=pid]').first().val();
    }
 
var updateBuyWithAffirmButton = function () {
        if ($productSetList.find('.add-to-cart[disabled]').length > 0) {
            $('#js-affirm-checkout-now').hide();
        } else {
            $('#js-affirm-checkout-now').show();
            document.dispatchEvent(new CustomEvent('affirm-checkout-button-rendered')); 
        }
            };
 
    // click on swatch for product set
    $productSetList.on('click', '.product-set-item .swatchanchor', function (e) {
        e.preventDefault();
        if ($(this).parents('li').hasClass('unselectable')) { return; }
        var url = Urls.getSetItem + this.search;
        var $container = $(this).closest('.product-set-item');
        var qty = $container.find('form input[name="Quantity"]').first().val();
 
        var oldSKU = $container.find('input[name=pid]').first().val();
        ajax.load({
            url: util.appendParamToURL(url, 'Quantity', isNaN(qty) ? '1' : qty),
            target: $container,
            callback: function () {
                updateAddToCartButtons();
                updateAffirmItems($container, oldSKU);
                updateBuyWithAffirmButton();
                tooltip.init();
            }
        });
    });
```

> 📘 Important
>
> To properly render “affirm-as-low” on product, landing, and card page, verify that the `affirm.ui.refresh` function is being called at the end of handle functions that influence price on basket (e.g., ajax product price change, quantity based price change, pagination, search, add-on price change).

<Callout icon="👍" theme="okay">
  Ensure to rebuild client JS after making modifications.
</Callout>

***

## Controllers (SiteGenesis Controllers)

### 1. cartridge/controllers/COBilling.js

Reassign `applicablePaymentMethods` within the `initCreditCardList` function with the following code:

```javascript COBilling.js
applicablePaymentMethods = require('*/cartridge/controllers/Affirm').Init(cart, applicablePaymentMethods);
```

![](https://files.readme.io/0235777-controllers_1_COBilling-1.png "controllers_1_COBilling-1.png")

In the same file, find the `publicStart` function and replace the `applicablePaymentMethods` assignment with the following code:

```javascript COBilling.js
var applicablePaymentMethods = require('*/cartridge/controllers/Affirm').Init(cart, creditCardList.ApplicablePaymentMethods);
```

![](https://files.readme.io/3d87682-controllers_1_COBilling-2.png "controllers_1_COBilling-2.png")

Then, replace the `status` assignment within the `resetPaymentForms` function with the following code:

```javascript COBilling.js
var status = Transaction.wrap(function () {
        if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals('PayPal')) {
            app.getForm('billing').object.paymentMethods.creditCard.clearFormElement();
            app.getForm('billing').object.paymentMethods.bml.clearFormElement();

            cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_CREDIT_CARD));
            cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_BML));
            cart.removePaymentInstruments(cart.getPaymentInstruments('Affirm'));
        } else if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals(PaymentInstrument.METHOD_CREDIT_CARD)) {
            app.getForm('billing').object.paymentMethods.bml.clearFormElement();

            cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_BML));
            cart.removePaymentInstruments(cart.getPaymentInstruments('PayPal'));
            cart.removePaymentInstruments(cart.getPaymentInstruments('Affirm'));
        } else if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals(PaymentInstrument.METHOD_BML)) {
            app.getForm('billing').object.paymentMethods.creditCard.clearFormElement();

            if (!app.getForm('billing').object.paymentMethods.bml.ssn.valid) {
                return false;
            }
            cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_CREDIT_CARD));
            cart.removePaymentInstruments(cart.getPaymentInstruments('PayPal'));
            cart.removePaymentInstruments(cart.getPaymentInstruments('Affirm'));
        } else if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals('Affirm')) {
        	cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_CREDIT_CARD));
            cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_BML));
            cart.removePaymentInstruments(cart.getPaymentInstruments('PayPal'));
        }
        return true;
    });

    return status;
```

![](https://files.readme.io/6f4c829-controllers_1_COBilling-3.png "controllers_1_COBilling-3.png")

### 2. cartridge/controllers/COPlaceOrder.js

Below the `saveCCResult` variable and its conditional check, include `affirmController` to get Affirm status with the following code snippet:

```javascript COPlaceOrder.js
var affirmController = require('int_affirm_controllers/cartridge/controllers/Affirm');
var affirmCheck = affirmController.CheckCart(cart);
if (affirmCheck.status.error) {
	return {
		error: true,
		PlaceOrderError: affirmCheck.status
	};
}
```

### 3. cartridge/controllers/COSummary.js

In the beginning of `submit` function, add the following code:

```javascript COSummary.js
var redirected = require('int_affirm_controllers/cartridge/controllers/Affirm').Redirect();
if (redirected) {
	return;
}
```

![](https://files.readme.io/6315f17-controllers_3_COSummary.png "controllers_3_COSummary.png")

***

## Pipelines (SiteGenesis Pipelines)

### 1. cartridge/pipelines/COBilling.xml

Find the start node `InitCreditCardList` and add a call node (Affirm-Init, int\_affirm\_pipelines) after the second Assign node.

<Image align="center" alt="InitCreditCardList to Script (bc_api). Then assign (bc_api), assign (bc_api). Affirm Init. SetFormOptions(bc_api). BindPaymentCardsToForm." caption="pipelines_1_COBilling_1_Affirm-Init" title="pipelines_1_COBilling_1.png" src="https://files.readme.io/34ccde1-pipelines_1_COBilling_1.png" width="smart" />

Then, find the start node `ResetPaymentForms` and add Pipelet nodes.

<Image align="center" alt={722} caption="pipelines_1_COBilling_2_ResetPaymentForms" title="pipelines_1_COBilling_2.png" src="https://files.readme.io/b0ff01a-pipelines_1_COBilling_2.png" />

In the same pipeline, create an additional branch:

* Decision node – Decision Key: `!CurrentForms.billing.paymentMethods.selectedPaymentMethodID.value.equals('Affirm')`
* ClearFormElement Pipelet – FormElement: `CurrentForms.billing.paymentMethods.creditCard`
* ClearFormElement Pipelet – FormElement: `CurrentForms.billing.paymentMethods.bml`
* `RemoveBasketPaymentInstrument` Pipelet – PaymentInstruments: `Basket.getPaymentInstruments(dw.order.PaymentInstrument.METHOD_CREDIT_CARD)`
* `RemoveBasketPaymentInstrument` Pipelet – PaymentInstruments: `Basket.getPaymentInstruments( dw.order.PaymentInstrument.METHOD_BML)`

<Image align="center" alt={745} caption="pipelines_1_COBilling_4_ResetPaymentForms_additional_branch" title="pipelines_1_COBilling_4.png" src="https://files.readme.io/dfce965-pipelines_1_COBilling_4.png" />

# Sibling pages

* [SFRA Cartridge Installation](https://docs.affirm.com/platforms/docs/sfcc-sfra.md)