Adding one-time products to existing subscriptions with the Recharge Theme Engine

Allow customers to add one-time products to their subscriptions through the customer portal using the Recharge Theme Engine.

Using the Recharge Theme Engine, you can enable your customers to add one-time products (OTP) to upcoming subscription orders through the customer portal. This guide outlines the steps to set this up.

Use the Recharge Theme Engine, to enable customers to add one-time products to upcoming subscriptions through the customer portal.

This guide provides instructions on how to allow customers to add one-time products through the customer portal using the Recharge Theme Engine.


Before you start

  • You must have access to the Recharge Theme Engine and be on the Recharge Pro plan or be a Recharge Agency Partner.
  • Recharge does not support these customizations as per the Recharge design and integration policy as it requires custom coding.
  • When a one-time purchase product is added to an existing subscription order, Shopify displays subscription and delivery verbiage on the one-time purchase product. However, the order processes the add-on product as a one-time purchase, not as a subscription product, ensuring the customer is only charged once for their add-on product.

Step 1 - HTML logic to set up a button

  1. In the merchant portal, click Storefront and select Theme Editor.
  2. Click Edit code for the theme you want to edit.
  3. Click on the subscription.html template file.
  4. Create a button that will toggle the product list container on click. Add the following code to set it up:
<!-- Add button that will toggle the product list container on click -->
 <p>
 <a href="#product-search-wrapper"class="btn" style="margin-bottom: 50px" onclick="ReCharge.Helpers.toggle('product-search-wrapper'); return false;" >Add something to subscription?</a>
 </p>

After setting up the code, you must create a product list container that will initially be hidden. Place it inside <div class="grid"> as the last element. Add the following code before moving on to the next step:

<!-- Create a wrapper div that will contain Onetime products -->
    <div class="grid__item medium-up--two-thirds">
      <div id="product-search-wrapper" style="display:none">
      </div>   
    </div>

Step 2 - JavaScript logic to run an Ajax call

Now that you have successfully created the HTML foundation, it's time to create a <script> tag and place it at the bottom of the template to house your JavaScript logic:

  1. Store subscription into a variable, so you can use its properties for creating one-time products.
  2. Get all available products using an Ajax call.
  3. After successfully fetching the products, call a function that will render all products. That function will do the following:
    1. Select product list container.
    2. Filter products to get only the ones that are sold as one-time.
    3. Loop through one-time products and create HTML structure for each product.
  4. In the HTML product structure, place an Add Product button on click handler. On click handler will collect all necessary data for creating one-time products, which includes:
    1. Shopify_variant_id
    2. Address_id
    3. Quantity
    4. next_charge_scheduled_at
  5. Build a data object to send in the Ajax call.
  6. Build an Ajax call and send a POST request to create the one-time product.
  7. After the one-time product is created, show a success message and reload the page.

Add the following code to accomplish the above:

<script>
  // store subscription into a variable
  let subscription = {{ subscription | json }};  // get all the products 
  $.ajax({
    url: `/tools/recurring/portal/{{ customer.hash }}/request_objects?token=${window.customerToken}`,
    type: 'get',
    dataType: 'json',
    data: { schema: '{ "products": { "base_source": "store_settings"} }' }
  }).done(function(response) {
    // Successful request made
    // call a function that will render one time products
    renderProducts(response.products)
  }).fail(function(response) {
    // Request failed
    console.log(response);
  });  // render onetime products
  function renderProducts(products){
	// select product list container
    let productSearchWrapper = document.querySelector('#product-search-wrapper');
    // filter products that are sold as onetimes
    let onetTimeProducts = products.filter(function(product){
       return product.subscription_defaults.storefront_purchase_options.includes('onetime') && ! product.shopify_details.title.includes('monthly');
    });    // loop through onetime products and create structure for each product
    onetTimeProducts.map(product => {
      let shopifyProductId = product.shopify_details.shopify_id;
      let shopifyVariantId = product.shopify_details.variants[0].shopify_id;
      let productTitle = product.shopify_details.title;
      let productPrice = product.shopify_details.variants[0].price;
      let imgSrc = product.shopify_details.image.src;     
      let nextChargeDate = subscription.next_charge_scheduled_at.split('T')[0];      // create HTML structure for the product
      productSearchWrapper.innerHTML += `
      <div>
		<div>
          ${ imgSrc
              ?	`<img src="${imgSrc}" style="width: 100px; height: 100px;">`
              :	'<img src="//static.rechargecdn.com/static/images/no-image.png" width="100">'
           } 
		</div>
		<p>${productTitle}</p>
		<p>$${productPrice}</p>
		<p>${nextChargeDate}</p>
		<p>
          <input type="number" value="1" name="quantity-${shopifyVariantId}">
		</p>
		<button class="btn" data-shopify_variant_id="${shopifyVariantId}" onclick="addOnetimeProductHandler(event)"> Add Product 
		</button>
      </div>										`
    });
  }  function addOnetimeProductHandler(event){
      event.preventDefault();
      event.target.innerText = 'Processing...';      // get shopify_variant_id for selected product
      let shopifyVariantId = event.target.getAttribute('data-shopify_variant_id');      
      // get quantity for selected product
      let quantity = document.querySelector(`[name=quantity-${shopifyVariantId}]`).value;        // build data object to send
      const dataToSend = {
         "shopify_variant_id": shopifyVariantId,
          "address_id": subscription.address_id,
          "quantity": quantity,
          "next_charge_scheduled_at": subscription.next_charge_scheduled_at
      }      // AJAX call for creating a Onetime product
      $.ajax({
        url: '/tools/recurring/portal/{{ customer.hash }}/onetimes',
        type: 'post',
        dataType: 'json',
        data: dataToSend
      }).done(function(response) {
        // Successful request made
        // show green success message
        ReCharge.Toast.addToast('success', `Onetime created: ${response.onetime.product_title}`);
        // reload page
        window.location.reload();
        console.log(response);
      }).fail(function(response) {
        // Request failed
        console.log(response);
      });     
    }
</script>

Step 3 - Use CSS to make the button responsive

Lastly, make the product list container responsive by using CSS and a display grid. Create a new tag you created in Step 2.

Add the following code:

<style>  
  #product-search-wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-gap: 1rem;	
  }
</style>

Need Help? Contact Us