@arc-core-components/feature_subscriptions

All subscribing, all the time.

Usage no npm install needed!

<script type="module">
  import arcCoreComponentsFeatureSubscriptions from 'https://cdn.skypack.dev/@arc-core-components/feature_subscriptions';
</script>

README

Arc Subscriptions

This is an out of the box solution for Arc Subscriptions that offers three different levels of components to suit your needs plus convenience functions.

  1. SPAs - these requires only minimal style customization and configuration with the option to pass in custom localization. It includes all of the workflows pre-built into two different components for you to get up and running on Arc Subscriptions as soon as possible with minimal effort. One is designed for logging in and user profile/account management and the other allows the user to purchase products and follow the checkout and payment workflow.

    • Identity - login, lost password, verify email, profile, manage subscriptions, order history

    • Retail - offers/products, cart, payment processing, order confirmation

    • Custom SPA - shows an example of how to create your own custom SPA

  2. Forms - these are pre-built forms for the different areas of Arc Subscriptions that you can highly customize or use as they are to create your own workflows or to meet the specific needs of your project.

    • Identity Forms - forms related to account creation and management, email verification and password resets

    • Retail & Sales Forms - forms offers/products and shopping cart

    • Paywall - all variations of the paywall forms and an example component

    • Custom Form - an example of how to create your own custom form

  3. Elements - these are all of the form sub-components that make up Arc Subscriptions so that you can create and build your highly customizable forms and workflows to meet the specific needs of your project

  4. Functions & Analytics - these are functions and events you can use to help with your custom form creations or for customizing a specific form.

    • Analytics - call these or listen to these for your analytics reporting

    • Input Validation - validations to run on various types of form inputs

    • Utilities - various other functions

Installation

Run npm i @arc-core-components/feature_subscriptions@stable to add it to your project.

In your default output file, add a script that sets window.arcIdentityApiOrigin, window.arcSalesApiOrigin, and window.arcSignInUrl. The arcIdentityApiOrigin and arcSalesApiOrigin will usually point to the same location. The arcSignInUrl should point to a page or template where you have implemented your signin feature.

For example here is a partial snippet:

// File: components/output-types/default.jsx

//set cc client side variables
let subsConfig = `window.arcIdentityApiOrigin = "YOUR_API_ORIGIN_URL";`;
subsConfig += `window.arcSalesApiOrigin = "YOUR_API_ORIGIN_URL";`;
subsConfig += `window.arcSignInUrl = "YOUR_SUBSCRIPTIONS_LOGIN_URL";`;

return (<html lang="en">
  <head>
    <script type="text/javascript" dangerouslySetInnerHTML={{ __html: subsConfig }} />
  </head>
  <body>
    ...

Identity

This allows you to use pre-built solutions for managing login/logout, lost password, user profile, viewing order history and managing subscriptions.

SPA

This is a Single Page App that has all of the pre-configured workflow already setup and ready for you to customize and start using.

Property Type Description
config (required) Object Configurations for the SPA
debug Boolean Toggle debugging messages inside of the SPA
localization Object Object with mappings to all the text, error codes and country codes so you can customize all text within the SPA
styles Object Object with mappings to all styles so you can customize the look and feel of the SPA

SPA Configuration

These are passed to the config property and will affect how your SPA works.

Property Type Description
dateFormat String Accepts EU or US
useCookies Boolean Whether or not to allow cookies (if disabled uses localstorage instead)
autoLogin Boolean If true will automatically login after creating a new account
secondLastName Boolean If true, displays a second last name field on the sign up and profile form
enableMagicLink Boolean Toggles showing the magic link button on the forgot password form
recaptcha Object Developer configuration settings for ReCaptcha
debug Boolean When true will console log debugging messages
termsUrl String Url to your terms of service
privacyUrl String Url to your privacy policy
GDPRUrl String Url to GDPR policies
registerUrl String Url to create a new account
signInUrl String Url to sign in
signOutUrl String Url to sign out page
signOutPageHomeUrl String Url to return to the home page once you've signed out
signOutPageSignInUrl String Url on the sign out page to sign in again
signOutPageSignUpUrl String Url on the sign out page to create a new account
profileUrl String Url to the user's profile page
useTermsModal Boolean If true, shows the terms of service in a modal window rather than a clickable URL
isTermsModalOpen Boolean Function
termsModalHideCloseIcon Boolean Whether or not to hide the terms of service close modal icon
termsModalChildren Object Content to display in the terms of service modal
showSignOutLink Boolean Whether or not to show the sign out link on the profile
hidePrivacyModalCloseIcon Boolean Whether or not to display the close modal icon in the privacy modal
googleButtonWidth Number The width of the google social sign in button
googleButtonHeight Number The height of the google social sign in button
googleButtonLongTitle Boolean Whether or not to use the short title or the long title on the google social sign in button
googleButtonTheme String The theme for the google button, defaults to "dark"
fbButtonSize String The size of the facebook button
fbButtonType String The type of facebook button
fbIncludePicture Boolean Whether or not to include a picture on the facebook button
dobRegistration Boolean Turn on requirements to be a certain age before you can register
dobRequiredRegistration Boolean Forces the date of birth field to be required on the registration form
hideFacebookOnSignIn Boolean Hides the button to sign in with Facebook on the sign in page
hideGoogleOnSignIn Boolean Hides the Google sign in button on the sign in page
hideFacebookOnSignUp Boolean Hides the register with Facebook button on the registration page
hideGoogleOnSignUp Boolean Hides the register with Google button on the registration page
registrationAge Number Sets the age a user must be in order to create an account, use 0 (default) if any age is allowed. For example, if this value is set to 14 then users that are 14 years and 1 day old will be permitted to register, anyone younger will not.
peekSignUpStateCallback Function A function intended to return the current state values to be used in defining new logic/properties outside of the sign up form, for instance setting profile attributes from selections in the privacy modal

Hash Routes

These are the hash routes used in the Identity SPA:

#/ or #/sign-in – login to an existing account or optionally use social logins (FB or Google)

#/register – creates a new account by filling in the form or optionally using social sign up buttons (FB or Google)

#/sign-out – destroys the user’s session and gives them options on where to go next

#/profile – Includes the change password form, allows the user to edit their account information they entered when they signed up, includes the privacy modal (if enabled) and a link to sign out.

#/manage – allows a user to manage their current, update their credit card, change their billing address, cancel a subscription and view their order history.

#/forgot-password – for users who have forgotten their password.

#/verify-email?nonce={nonce} – if enabled in the subs admin, will force users to verify their email address before they can proceed to their profile page.

#/reset-password?nonce={nonce} - allows the user to reset their password if the nonce is valid.

#/magic-link?nonce={nonce} – allows the user to send themselves a one-time access link to login without having to remember their password if the nonce is valid.

SPA Sample Implementation

/* eslint-disable */
import React from 'react';
import Subscriptions from '@arc-core-components/feature_subscriptions';

/**
* @class IdentitySPA
* @desc Single page application for subscriptions Sign In, Sign Up, Profile,
* Reset Password, and Magic Link functionality
*
* @return {Object} IdentitySPA
*/
class Identity extends React.PureComponent {

  /**
  * Render the Identity SPA
  **/
  render() {
    return (<Subscriptions.IdentitySPA
      debug={false}
      styles={{
        wrapperClasses: 'col',
        profilePageContainerClasses: 'width_full',
        managePageContainerClasses: 'width_full',
        headerClasses: '',
        errorClasses: 'row color_red',
        errorHeadlineSize: 4,
      }}
      config={{
        useCookies: true,
        autoLogin: true,
        secondLastName: false,
        recaptcha: {
          enabled: true,
          token: "your_site_key_here",
          position: "above",
          size: "invisible",
        },
        termsUrl: "http://my.site.com/terms",
        privacyUrl: "http://my.site.com/privacy",
        GDPRUrl: "http://my.site.com/gdpr",
        signOutUrl: '/sign-out',
        signOutPageHomeUrl: '/homepage',
        signOutPageSignInUrl: '/sign-in',
        signOutPageSignUpUrl: '/register',
        profileUrl: '/profile',
        dobRegistration: true,
        dobRequiredRegistration: true,
        googleButtonWidth: 400,
        googleButtonHeight: 30,
        registrationAge: 14,
      }}
    />);
  }
}

export default Identity;

SPA Localization

All text within the SPA can be localized by passing a custom localization object with alternative/translated content. For a full list of all text that can be customized please see the localization section below.

SPA Styles

By default we provide bootstrap classes on all components for you. In order to match the look and feel of your project we expose certain classes within the SPA that you can set to your existing CSS classes. For a full list of all styles that can be customized please see the styles section below.

Identity Forms

  1. Sign Up - Create a new account
  2. Sign In - Login to your existing account
  3. Profile - View/edit user profile
  4. Manage - View order history and see/cancel current subscription
  5. Email Verification - Allow users to verify their email address
  6. Forgot Password - Request a link to change the user's password
  7. Reset Password - Allow a verified user to set a new password
  8. Order History - A table to display/sort orders
  9. Change Password - A form for changing the user's profile after they've logged in
  10. Magic Link - A form to get a one time login magic link code
  11. Sign Out - A form to destroy the user's session and let them sign in again or create a new account

Sign Up Form

Allows a user to create a new account on your site

Property Type Description
useCookies (required) Boolean Whether or not to use cookies
doLogin (required) Boolean If true, signs the user in after a successful sign up
successUrl (required) String Where the user should be redirected to after a successful sign up
headerProps Object Props to pass to the Header form element at the top of the profile form
saveChangesProps Object Props to pass to the SaveChanges form submit button
firstNameProps Object Props to pass to the FirstNameInput form element
lastNameProps Object Props to pass to the LastNameInput form element
signInButtonProps Object Props to pass to the SignInButton form element
errorText Object Localization of all SDK API error messages
<SignUpForm
  useCookies={false}
  doLogin={true}
  successUrl="http://my.site.com/profile"
/>

Sign In Form

Allows a user with an existing account to login to that account

Property Type Description
loginSuccessUrl (required) String URL to redirect the user to upon successful login, defaults to /profile )
cookie Boolean Use cookies to store user authentication
recaptchaToken String The ReCaptcha token
wrapperClasses String Classes to apply around the sign in form
errorClasses String Classes to apply to sign in form error messages
headerProps Object Props to pass to the Header form element at the top of the sign up form
emailInputProps Object Props to pass to the EmailInput form element
passwordInputProps Object Props to pass to the PasswordInput form element
forgottenPasswordLinkProps Object Props to pass to the ForgotPasswordLink form element
signInButtonProps Object Props to pass to the SignInButton form element
rememberMeCheckboxProps Object Props to pass to the RememberMeCheckbox form element
errorText Object Localization of all text and error codes
<SignInForm
  loginSuccessUrl="http://my.site.com/profile"
/>

Profile Form

Allows a user to view and edit their profile information

Property Type Description
wrapperClasses String the classes to use on around form elements
headerProps Object properties to pass to the Header form element
firstNameProps Object properties to customize the FirstNameInput element
lastNameProps Object properties to customize the LastNameInput element
secondLastNameProps Object properties to customize the SecondLastNameInput element
displayNameProps Object properties to customize the DisplayName element
emailInputProps Object properties to customize the EmailInput element
DOBInputProps Object properties to customize the DOBInput element
genderRadioButtonsProps Object properties to customize the GenderRadioButtons element
addressContainerProps Object properties to customize the AddressContainer element
validateFullAddressCallback Function custom function that validates the address
saveChangesButtonProps Object properties to customize the SaveChangesButton element
errorClasses String the classes applied to the error messages displayed in case of failure
successClasses String the classes applied to the success messages displayed in case of success
errorText Object Localization of all SDK API error messages
successText Object Localization of all SDK API success messages
getProfileFailureUrl String the url to redirect the user in case of failure to retrieve the user profile
requireEmailVerification Boolean the condition of requiring the user to verify their email address before accessing their profile information (defaults to false)
verifyEmailUrl String the url to redirect the user in the case that the email is not verified
<ProfilePageForm
  headerProps={{
    title: "Profile Page",
  }}
  DOBInputProps={{
    dateFormat: "US",
  }}
  getProfileFailureUrl="http://my.site.com/login"
  verifyEmailUrl="http://my.site.com/verify-email"
/>

Manage Form

Allows a user to view and/or cancel their subscription and see their latest order history

Property Type Description
localization (required) Object Object containing all localization values passed from the SPA
styles Object Object containing all style customization prop values
<ManageSubsPage
  localization={localizationObject}
  styles={stylesObject}
/>

Email Verification Form

Allows a user to verify they are using a valid email address.

Property Type Description
successUrl String Redirect to this URL on successful verification
successText String Message to display on successful verification
nonce String A nonce to verify the email through the user's email provider
<EmailVerification
  successUrl="/#profile"
/>
How To Verify An Email Address

When you set the verify email address flag to true in the Subs admin the IdentitySPA and the Login Form will require a verified email addresses before the user can login and view their profile or manage their subscription. Upon account creation an email must be set to the user's unverified email address and include a link back to the following URL pattern in order to complete the full verification process: https://my.site.com/{subscriptions-url}?nonce={nonce}/#verify

Forgot Password Form

Allows a user to request a link to reset their password

Property Type Description
header String the forgot password header text
instructions String instructions on how to fill out the form to get a reset password link
emailInputProps Object properties to pass to the EmailInput form element
resetLinkButtonProps Object properties to pass to the ResetLinkButton form element
magicLinkButtonProps Object properties to pass to the MagicLinkButton form element
wrapperClasses String classes wrapper around form
instructionClasses String classes that wrap around the instructions for this form
errorClasses String classes for error messages
successClasses String classes for success messages
defaultResponseText Object an object with all default text, error codes and response messages to override the default values
<ForgotPasswordForm
  header="Forgot Your Password?"
  instructions="Enter your email address and click the button to send a reset password link to your email"
/>

Reset Password Form

Allows a user to reset their password. Requires an email be sent to the user with a nonce in the format of https://my.site.com/{subscriptions-url}?nonce={nonce}/#reset-password

React Prop Type Description
successUrl (required) String Where the user is redirected after they successfully change their password
nonce String optional nonce to use in place of the nonce coming from the window URL hash
header String the forgot password header text
loading String message to display while the component is loading
wrapperClasses String classes wrapper around form
loadingClasses String properties to pass to the EmailInput form element
headerClasses String classes to pass to the header
passwordProps Object properties to pass to the first password input
confirmPasswordProps Object properties to pass to the second confirm password input
resetButtonProps Object properties to pass to the reset password button at the bottom of the form
instructionClasses String classes that wrap around the instructions for this form
errorClasses String classes for error messages
successClasses String classes for success messages
defaultText Object an object with all default text, error codes and response messages to override the default values
<ResetPasswordForm
  successUrl="http://my.site.com/login"
/>

Manage Subscriptions Status Bar

Property Type Description
statusLabel String, JSX Element Status Label
statusInputPlaceholder String Status Input Placeholder
statusRenderInputContent Function Status Render Input Content
actionLabel String, JSX Element Action Label
actionInputPlaceholder String Action Input Placeholder
actionCancelReasons Array Action Cancel Reason Options List
actionBarCancelSubscriptionButtonContent String, JSX Element Action Bar Cancel Subscription Button Content
actionBarSubmitButtonContent String, JSX Element Action Bar Submit Button Content
actionBarCancelButtonContent String, JSX Element Action Bar Cancel Button Content
actionBarRenewSubscriptionButtonContent String, JSX Element Action Bar Renew Subscription Button Content
actionBarRenewSubscriptionRenderLabelContent Function Action Bar Renew Subscription Render Label Content
actionBarShowSubscriptionsButtonContent String, JSX Element Action Bar Show Subscriptions Button Content
actionBarShowSubscriptionsLabelContent String, JSX Element Action Bar Show Subscriptions Label Content
actionAlertSuccessContent String, JSX Element Action Alert Success Content
actionAlertErrorContent String, JSX Element Action Alert Error Content
actionAlertDuration Number Action Alert Duration
loadingIndicator String, JSX Element Loading Indicator
renewSubscriptionCampaignID String, Number "Renew Subscription" CampaignID
showSubscriptionsCampaignID String, Number "Show Subscription" CampaignID
renewSubscriptionURL String Renew Subscription URL
showSubscriptionOptionsURL String Show Subscription Options URL
styles Object Object containing all custom class and styling props
<ManageSubStatusBar
  statusLabel="Current Subscription"
  statusInputPlaceholder=""
  statusRenderInputContent={subObj => `${subObj.productName} - ${subObj.subscriptionID}`}
  actionLabel="Reason for Cancellation"
  actionInputPlaceholder="Select reason..."
  actionCancelReasons={[
    "Price is too expensive"
    "Didn't utilize my subscription enough"
    "Found a service I like better"
    "I just don't want it anymore"
  ]}
  actionBarCancelSubscriptionButtonContent="Cancel Subscription"
  actionBarSubmitButtonContent="Submit & Cancel"
  actionBarCancelButtonContent="Nevermind"
  actionBarRenewSubscriptionButtonContent="Renew subscription"
  actionBarRenewSubscriptionRenderLabelContent={(subObj) => {
    return (<><i className="mr-2">{subObj.productName}</i>subscription has expired</>);
  }}
  actionBarShowSubscriptionsButtonContent="Show subscription options"
  actionBarShowSubscriptionsLabelContent={(<i className="mr-2">No current subscriptions</i>)}
  actionAlertSuccessContent="Subscription Cancellation Successful!"
  actionAlertErrorContent="Subscription Cancellation Failed"
  actionAlertDuration={4000}
  loadingIndicator={(<div className="alert alert-warning">Loading data...</div>)}
  renewSubscriptionCampaignID={12345}
  showSubscriptionsCampaignID={67890}
  renewSubscriptionURL="/packages-feature-subscriptions-src-children-retail-spa-spa#/campaign"
  showSubscriptionOptionsURL="/packages-feature-subscriptions-src-children-retail-spa-spa#/campaign"
  styles={stylesObject}
/>

Order History Table

Creates a responsive, sortable table of the user's order history with expandable rows for overflowing fields (dynamic/responsive)

Property Type Description
title String Title to use on the table
displayConfig Function Function that returns a configuration object used for providing table display parameters to child Table component
styles Object Object containing all custom class and styling props
<OrderHistoryTable
  title="Table Title"
  styles={stylesObject}
  displayConfig={displayType => ({
    mobile: {
      numColumns: 2,
      displayClasses: `d-${displayType} d-sm-${displayType}`,
      hiddenClasses: "d-none d-sm-none",
    },
    tablet: {
      numColumns: 3,
      displayClasses: `d-md-${displayType}`,
      hiddenClasses: "d-md-none",
    },
    desktop: {
      numColumns: 5,
      displayClasses: `d-lg-${displayType} d-xl-${displayType}`,
      hiddenClasses: "d-lg-none .d-xl-none",
    },
  })}
/>

Change Password Form

This allows a user to change their password to their account. They must already be signed into their account in order to use this form.

Property Type Description
header String Title shown above the form
headerClasses String Classes to apply to the header text
loading String Text to display for the loading message
loadingClasses String Classes to apply to the loading text
wrapperClasses String Classes to apply to the div around the form
errorClasses String Classes to apply to the error messages return from the form
successClasses String Classes to apply to the success messages return from the form
oldPasswordProps Object Values passed to the passwordInput component used for the old password input field
newPasswordProps Object Values passed to the passwordInput component used for the new password input field
confirmPasswordProps Object Values passed to the passwordInput component used for the confirm password input field
saveChangesButtonProps Object Values passed to the button component used to save changes
defaultText Object Localzation of text and error codes
<ChangePassword
  loading="Loading, please wait..."
  successClasses: "text-success mb-2",
  newPasswordProps: {{
    label: "Enter Your New Password",
  }}
/>

Magic Link Form

This allows a user to request a one time access code to login to their account if they've forgotten their password. It requires a nonce be sent to them with a link to their email address in the format of https://my.site.com/{subscriptions-url}?nonce={nonce}/#magic-link

React Prop Type Description
wrapperClass String Classes applied to the wrapper div
successUrl String The url to redirect to when a successful nonce is verified
successText String The text to show when a nonce is successfully verified
nonce String The nonce to verify (will check from the URL by default but can also be passed as a prop)

<MagicLinkForm />

Sign Out Form

When a user is logged in and this form is rendered this form will automatically clear their session and display options to allow them to log back in, create a new account, or go back to the homepage.

React Prop Type Description
pageText String Text to show above the links on the page
wrapperClasses String Classes to apply to the wrapper div
textClasses String Classes to apply to the text at the top of the sign out form
homeText String The text for the home url link
homeUrl String The url for redirecting the user back to the homepage
redirectLinkHomeClasses String Classes to apply to the homepage redirect link
signInText String The text for the sign in url link
signInUrl String The url for redirecting the user to the sign in page
redirectLinkSignInClasses String Classes to apply to the sign in url
signUpText String The text for the sign up url link
signUpUrl String The url for redirecting the user to the sign up page
redirectLinkSignUpClasses String Classes to apply to the sign up url
<SignOutForm
  pageText="You have successfully logged out!"
/>

Google Sign On

Creates an out-of-the-box Sign On with Google feature.

Required Setup
  1. The correct google ID must be setup in the Subs Admin for the particular tenant (apiOrigin).
  2. The component must be placed on a valid https page. (For local development ngrok was used).
React Prop Type Description
config Object the object containing all the configurations for the GoogleSignOn feature.
styles Object the object containing all the styling classes for the GoogleSignOn feature.
text Object the object containing all the error messages for the Google Sign On Feature
redirectLink String the url to which the user gets redirected after a successful Google sign on.
googleButtonWidth Number width in pixels accepted by the Google Sdk (Defaults to 120).
googleButtonHeight Number height in pixels accepted by the Google Sdk (Defaults to 36).
googleButtonLongTitle Boolean boolean value that sets whether to display long labels such as "Sign in with Google" rather than "Sign in" (Defaults to false).
googleButtonTheme String The color theme of the button: either light or dark (defaults to "light").
wrapperClasses String classes to style the button wrapper
sdkErrorClasses String classes to style the error message in case the google sdk fails to load.
signOnErrorClasses String classes to style the error message in case the google Sign in fails.
successCallback Function function that is invoked after a succesful sign on
failureCallback Function function that is invoked after a failed sign on
attemptCallback Function function that is invoked after the button click
sdkErrorMessage String error message to be displayed in case the Google sdk fails to load
signOnErrorMessage String error message to be displayed in case the Google sign on fails for any reason
Additional Customizations

More Google button customization options are available on https://developers.google.com/identity/sign-in/web/reference#ui_elements

  <GoogleSignOn
  config= {{
    redirectLink: "/profile",
    googleButtonWidth: 400,
    googleButtonHeight: 40,
    googleButtonLongTitle: true,
    googleButtonTheme: "dark",
  }}
  styles={{
    wrapperClasses: "d-flex flex-column align-items-center justify-content-center mt-3",
    sdkErrorClasses: "text-danger",
    signOnErrorClasses: "text-danger",
  }}
  />

Facebook Sign On

Creates an out-of-the-box Sign On with Facebook feature.

Required Setup
  1. The correct facebook APP ID and App Secret must be setup in the Subs Admin for the particular tenant (apiOrigin).
  2. The component must be placed on a valid https page. (For local development ngrok was used).
React Prop Type Description
config Object the object containing all the configurations for the FacebookSignOn feature.
styles Object the object containing all the styling classes for the FacebookSignOn feature.
text Object the object containing all the error messages for the FacebookSignOn Feature
redirect String the url to which the user gets redirected after a successful facebook sign on.
fbButtonSize String the size of the button configured by the facebook sdk, can be "large", "medium" or "small". Defaults to "large".
fbButtonType String the button text accepted by the facebook sdk, must be either "login_with" or "continue_with". Defaults to "login_with".
fbIncludePicture Boolean a boolean value accepted by the facebook sdk that allows the component to either include the name and profile picture when the user is signed into Facebook or not.
wrapperClasses String classes to style the button wrapper
sdkErrorClasses String classes to style the error message in case the Facebook sdk fails to load.
signOnErrorClasses String classes to style the error message in case the Facebook Sign On fails.
successCallback Function function that is invoked after a succesful sign on
failureCallback Function function that is invoked after a failed sign on
attemptCallback Function function that is invoked after the button click
sdkErrorMessage String error message to be displayed in case the Facebook sdk fails to load
signOnErrorMessage String error message to be displayed in case the Facebook Sign On fails for any reason
  <FacebookSignOn
  config={{
    fbButtonSize: "medium",
    fbButtonType: "continue_with",
    }}
  styles={{
    wrapperClasses: "border border-success d-flex justify-content-center"
  }}
  />

Retail & Sales

This allows you to use pre-built solutions for managing campaigns, displaying products, adding/removing items to your cart, payment processing and confirmation of your purchases.

SPA Configuration

This is a Single Page App that has all of the pre-configured workflow already setup and ready for you to customize and start using.

Property Type Description
useCookies (required) Boolean Whether or not to use cookies
autoLogin Boolean Automatically login via a cookie or local storage
checkoutUrl String Hash fragment used when user selects an offer
loginUrl String Hash fragment used when user needs to login
addItemToCartFailureUrl String Hash fragment used when we encounter an error adding an item to the cart
confirmationPageRedirectLinkURL String Hash fragment used when user clicks the "Start Reading" button on the confirmation page
errorUrl String Hash fragment used when an error is encountered
confirmUrl String Hash fragment used when user has paid
offersUrl String Hash fragment used when the user is looking at offers
recaptcha (required) Object Config for recaptcha
registerUrl String Hash fragment used when user wants to create a new account

Hash Routes

These are the hash routes used in the retail SPA:

#/ and #/campaign – This pulls up the campaign to display the products and subscriptions available for purchase. This uses data from a content source.

#/checkout – This displays the items currently in the user’s shopping cart.

#/confirmation – This displays a confirmation message after the payment has been successfully processed by the payment processor.

#/error – This is the default error message if something has gone wrong at any point during this process.

SPA Sample Usage

This will create the retail spa with debugging messages.

import Subscriptions from '@arc-core-components/feature_subscriptions';

<Subscriptions.RetailSPA
  debug={true}
  styles={this.props.styles}
  config={this.props.configuration}
  localization={this.props.translatedMessages}
  offersData={this.props.offers}
/>

SPA Localization

All text within the SPA can be localized by passing a custom localization object with alternative/translated content. For a full list of all text that can be customized please see the localization section below.

SPA Styles

By default we provide bootstrap classes on all components for you. In order to match the look and feel of your project we expose certain classes within the SPA that you can set to your existing CSS classes. For a full list of all styles that can be customized please see the styles section below.

Retail Forms

  1. Campaign - displays a list of the current products in a campaign
  2. Cart - displays a list of items in the user's shopping cart along with

Campaign Form

Allows a user to view the information related to a campaign in form of a headline image, headline text and one or several products. If a product contains several rates, the user has the option to select one, the product then being added to the user cart.

Property Type Description
offersObject (required) Object The object that contains all the offer related information that has been set up in the Subscriptions Admin
productCardsContainerProps Object Properties (text and styles) applied to the productCardsContainer element
headlineImageProps Object Properties (text and styles) applied to the HeadlineImage element
disclaimerMessageProps Object Properties (text and styles) applied to the Header (Disclaimer Message) component
checkoutUrl String The url of the checkout page where the user is able to interact with the current contents of their cart
loginUrl String The url of the login page where the user can either login or register and then proceed to checkout without losing the contents of their cart
addItemToCartFailureUrl String The url of the page on which the user lands in case the add to cart process fails
  <Subscriptions.Campaign
    checkoutUrl= "/checkout"
    loginUrl= "/login"
    addItemToCartFailureUrl= "/404"
    headlineImageProps={{
      wrapperClasses: String
      wrapperStyles: Object,
      smBrkPnt: String,
      mdBrkPnt: String,
      pageTitleSize: Number,
      titleClasses: String,
      pageSubTitleSize: Number,
      subTitleClasses: String,
    }}
    productCardsContainerProps= {{
      cardsWrapper: String,
      productCardsProps: {
        wrapperClasses: String,
        headerProps: {
          className: String,
        },
        unorderedListProps: {
          wrapperClasses: String,
          itemClasses: String,
        },
        getOfferButtonProps: {
          className: String,
        },
        lazyLoadingImageProps: {
          wrapperClasses: String,
          imageSrc: String,
          imageAlt: String,
          imageClasses: String,
          loaderSrc: String,
          loaderAlt: String,
          loaderClasses: String,
        },
        productPriceRadioButtonsProps: {
          wrapperClasses: String,
          label: String,
          labelClasses: String,
          radioContainerClasses: String,
        },
        singleProductPriceFieldProps: {
        size: Number,
        className: String,
        },
      },
    }}
    disclaimerMessageProps= {{
      size: Number,
      children: String,
      className: String,
    }}
    offersObject= {OFFERS_OBJECT}
  />

Notes on the OFFERS_OBJECT

In the current implementation with Fusion, the offers object is returned by making an api call that uses the campaign-name as a URI parameter, which is editable via a custom field.

A resolver needs to be setup in the sources folder, that would handle the API assembly logic.

const resolve = (params) => {
  const website = params['my-site'];
  const { 'campaign-name' } = params;
  return `https://api-${org}-${website}.cdn.arcpublishing.com/retail/public/v1/offer/live/${campaign-name}`;
};

export default {
  resolve,
  params,
};

The resolver can then be called inside the feature, passing the returned offers object to the Campaign component as a property.

    ...
    const { cached, fetched } = this.getContent('offers', { campaignName: props.customFields.defaultCampaign });
    this.state = { offers: cached };
    fetched.then((offers) => {
      this.setState({ offers });
    });
    ...
    ...
    ...
    render(){
      return(
        <Subscriptions.Campaign
          ...
          ...
          offersObject= {this.state.offers}
        />
      )
    }
    ...

Cart Form

This will quickly create a cart that displays a user's items they are trying to purchase, allows them to remove items, and provides a summary for the cost of those items.

Property Type Description
config (required) Object An object containing the cart's configuration
localization Object An object that includes the default text and error messages for the cart
styles Object An object with style classes
import Subscriptions from '@arc-core-components/feature_subscriptions';

<Subscriptions.Cart />

Paywall

The paywall is comprised of two parts:

  1. A portal component that loads the paywall script (p.js) and runs it to check the user's registration status.

  2. A modal with contents, buttons, links etc wrapped by the portal.

If you want the prebuilt modal contents you can pass a PaywallModal component into the PaywallPortal. Otherwise pass in your own children if you need something more flexible. It's pretty barebones so this will most likely happen.

Paywall Domains

  1. apiOrigin and paywallScriptSrc should look like the following:
  • "paywallScriptSrc": "https://my.site.cdn.arcpublishing.com/arc/subs/p.js"

  • "paywallApiOrigin": "//api-my.site.cdn.arcpublishing.com"

  1. If you add https:// to the paywallApiOrigin ArcP which is the paywall script adds https twice, it will break.

  2. The domain of paywallScriptSrc and paywallApiOrigin have to be the same, if they're not you get a CORS error and you'll get an error that the entitlement check failed (this is an under the hood request ArcP makes to the sales api to determine user registration).

  3. You will need versions of these url's for both sandbox and production. It's best to keep them in site properties for each of your sites.

Paywall p.js Script (ArcP)

  1. This will be located at https://my.site.cdn.arcpublishing.com/arc/subs/p.js

  2. If it's not available you will need to have it provisioned by the Arc Delivery team (DEL - minimum 1 week lead time).

  3. The p.js script is generated when you activate a paywall in the subs admin. Only one paywall can be active at a time. When you activate a new paywall you will get a new p.js. If you don't and the rules aren't updating when you run ArcP on the frontend, contact arc subscriptions support.

  4. You can type ArcP into the browser console to make sure the p.js script loaded (loaded by PaywallPortal via the paywallScriptSrc) properly.

  5. You can furthermore type ArcP._facts or ArcP._rules to see what facts were collected about the user and which paywall ruleset is being loaded from the admin. The _facts and _rules are compared by p.js to determine if the paywall should be triggered or not. If the paywall is triggered the modal will appear, and then the callback you entered thru the paywallFunction prop will also fire. You can also pass in the resultsCallback prop if you want anything else to happen after ArcP runs regardless whether the paywall is triggered or not as a follow up.

Setting Up A The Paywall In Fusion

Create a wrapper component and collect the necessary props the paywall needs for ArcP to run including:

  1. paywallScriptSrc - the source of your p.js file

  2. contentType - from globalContents - from the type field

  3. contentSection - from globalContents - see below, it should come from the websites field

  4. contentId - from globalContents - either the source id or the canonical_url

  5. Without the above props ArcP cannot function

PaywallPortal Props

React Prop Type Description
paywallScriptId (required) String A unique id for the p.js script tag.
paywallScriptSrc (required) String A src url to get p.js from the cdn.
contentType (required) String from globalContents - from the type field.
contentSection (required) String from globalContents - see below, it should come from the websites field.
contentId (required) String from globalContents - either the source id or the canonical_url.
paywallFunction Function Whatever you want to happen when ArcP triggers the paywall. The modal always pops up regardless of what function you pass in here.
customSubCheck Function Passed to ArcP if you want to override the default subscription status check.
customRegCheck Function Passed to ArcP if you want to override the default logged in status check.
resultsCallback Function Whatever you want to happen after ArcP.run returns.
children Elements You can pass in your own child elements instead of the contents of PaywallModal.

PaywallPortal Configuration

Example of how to setup the PaywallPortal with premade PayallModal contents and the IconButton (X button):

import Subscriptions from '@arc-core-components/feature_subscriptions';

  <Subscriptions.PaywallPortal
    overlayClassName="modal-dialog"
    contentClassName="modal-content modal-body"
    paywallScriptId="pjs-script"
    paywallScriptSrc="https://my.site.cdn.arcpublishing.com/arc/subs/p.js"
    contentType="your-content-type"
    contentSection="your-content-section"
    contentId="your-story-content-id"
    >
    <Subscriptions.IconButton
      iconClassName="close"
      onClickHandler={
        () => (
          window.location.href = 'your-close-modal-redirect-location'
        )
      }
      keyString="Escape"
      >
        <span>&times;</span>
    </Subscriptions.IconButton>
    <Subscriptions.PaywallModal
      styles={
        {
          paywallModalHeaderClassName: "",
          paywallModalHeaderSize: 2,
          paywallModalSubheaderClassName: "",
          paywallModalDescriptionClassName: "",
          paywallModalOfferClassName: "",
          paywallModalAllOffersClassName: "",
          paywallModalSignInClassName: "",
          paywallModalSignInMessageClassName: "",
        }
      }
      text={
        {
          paywallModalHeaderText: "Welcome to the PayWall",
          paywallModalSubheaderText: "You are out of free stories.",
          paywallModalDescriptionText: "Subscribe for $25 per month.",
          paywallModalOfferText: "Subscribe",
          paywallModalAllOffersText: "See all offers",
          paywallModalSignInText: "Sign In.",
          paywallModalSignInMessageText: "Already have an account? ",
        }
      }
      links={
        {
          paywallModalOfferUrl: "your-retail-SPA-url/#campaign?code=your-campaign-code",
          paywallModalAllOffersUrl: "your-default-offer-retail-SPA-url/#campaign",
          paywallModalSignInUrl: "your-identity-SPA-url/#sign-in",
        }
      }
      >
    </Subscriptions.PaywallModal>
  </Subscriptions.PaywallPortal>

Example Paywall Component

This is an example of an implementation of a paywall component in a fusion feature pack repository. It should be added to all article templates and pages that should trigger a paywall.

import React, { PureComponent } from 'react';
import Subscriptions from '@arc-core-components/feature_subscriptions';
import PropTypes from 'fusion:prop-types';
import Consumer from 'fusion:consumer';

/**
* We don't ever want to render the paywall server side
* so we can use this function to make sure we exit the
* render loop if this is a server side render
**/
function isServerSide() {
  return typeof window === 'undefined';
}

@Consumer
class Paywall extends PureComponent {

  /**
  * Handle the redirect back to a page that doesn't need
  * a subscription when they close the paywall modal
  **/
  handleRedirect = () => {
    const {
      customFields: {
        paywallIconButtonUrl,
      },
    } = this.props;
    window.location.href = paywallIconButtonUrl;
  };

  /**
  * This is the logic (or something similar) you'll need to implement
  * in order to remove or hide the article body from the DOM when the
  * paywall is triggered
  **/
  hideArticleBody = () => {
    alert('TODO: update hide article body logic');

    /* if (document.querySelector('your_article_body_parent_class') && document.querySelector('your_article_body_class')
    && !this.props.isAdmin) {
      const article = document.querySelector('your_article_body_parent_class');
      const body = document.querySelector('your_article_body_class');
      article.removeChild(body);
    } */
  }

  /**
  * Our recommendation is that you display the modal inline when you are in PB admin
  * to prevent it from covering content on your page. This applies to the modal overlay.
  **/
  getModalOverlayClasses = (isAdmin) => {
    return isAdmin ? 'background_gray flex align_items_center justify_center'
      : 'fullscreen fixed background_gray flex align_items_center justify_center z_index_highest';
  }

  /**
  * Our recommendation is that you display the modal inline when you are in PB admin
  * to prevent it from covering content on your page. This applies to the content in the modal.
  **/
  getModalContentClasses = (isAdmin) => {
    return isAdmin ? 'flex container_column justify_space_between background_white paywall paywall-modal_container'
      : 'flex container_column justify_space_between background_white paywall paywall-modal_container relative';
  }

  /**
   * Render the paywall component
   */
  render() {
    const { arcSite, isAdmin } = this.props;
    const { paywallScriptSrc } = getProperties()[arcSite]; // or wherever this is defined in your site properties JSON files
    const {
      _id: contentId,
      type: contentType,
    } = this.props.globalContent;
    const {
      websites: {
        [arcSite]: {
          website_section: {
            _id: sectionId,
          },
        },
      },
    } = this.props.globalContent;
    const modalOverlayClasses = this.getModalOverlayClasses(isAdmin);
    const modalContentClasses = this.getModalContentClasses(isAdmin);
    const iconClassName = 'modal_icon pointer absolute';
    const iconKeyString = 'Escape';
    const paywallScriptId = 'pjsscript';
    const paywallModalHeaderClassName = 'paywall-modal_header';
    const paywallModalSubheaderClassName = 'paywall-modal_sub-header';
    const paywallModalDescriptionClassName = '';
    const paywallModalOfferClassName = 'button button_primary';
    const paywallModalAllOffersClassName = '';
    const paywallModalSignInMessageClassName = '';
    const paywallModalSignInClassName = '';
    const paywallModalSignUpMessageClassName = '';
    const paywallModalSignUpClassName = '';
    const {
      customFields: {
        paywallModalHeaderText,
        paywallModalSubheaderText,
        paywallModalDescriptionText,
        paywallModalOfferText,
        paywallModalAllOffersText,
        paywallModalSignInText,
        paywallModalSignInMessageText,
        paywallModalOfferUrl,
        paywallModalAllOffersUrl,
        paywallModalSignInUrl,
        paywallModalSignUpMessageText,
        paywallModalSignUpUrl,
        paywallModalSignUpText,
      },
    } = this.props;

    return !paywallScriptSrc || isServerSide() ? null : (
      <Subscriptions.PaywallPortal
        overlayClassName={modalOverlayClasses}
        contentClassName={modalContentClasses}
        paywallScriptId={paywallScriptId}
        paywallScriptSrc={paywallScriptSrc}
        contentId={contentId}
        contentType={contentType}
        contentSection={sectionId}
        paywallFunction={this.hideArticleBody}
      >
        <Subscriptions.IconButton
          iconClassName={iconClassName}
          onClickHandler={this.handleRedirect}
          keyString={iconKeyString}
        >
          <span>&times;</span>
        </Subscriptions.IconButton>
         <p
          className={paywallModalHeaderClassName}
        >
          {paywallModalHeaderText}
        </p>
        <p
          className={paywallModalSubheaderClassName}
        >
          {paywallModalSubheaderText}
        </p>
        <p
          className={paywallModalDescriptionClassName}
        >
          {paywallModalDescriptionText}
        </p>
        <a
          href={paywallModalOfferUrl}
          className={paywallModalOfferClassName}
        >
          {paywallModalOfferText}
        </a>
        <p className="text text_align_center">
          <a
            href={paywallModalAllOffersUrl}
            className={paywallModalAllOffersClassName}
          >
            {paywallModalAllOffersText}
          </a>
        </p>
        <div className="text text_align_center margin_center">
          <p
            className={paywallModalSignInMessageClassName}
          >
            {paywallModalSignInMessageText}
            &nbsp;
            <a
              href={paywallModalSignInUrl}
              className={paywallModalSignInClassName}
            >
              {paywallModalSignInText}
            </a>
          </p>
          <p
            className={paywallModalSignUpMessageClassName}
          >
            {paywallModalSignUpMessageText}
            &nbsp;
            <a
              href={paywallModalSignUpUrl}
              className={paywallModalSignUpClassName}
            >
              {paywallModalSignUpText}
            </a>
          </p>
        </div>
      </Subscriptions.PaywallPortal>
    );
  }
}

Paywall.propTypes = {
  arcSite: PropTypes.string,
  isAdmin: PropTypes.boolean,
  globalContent: PropTypes.object,
  customFields: PropTypes.shape({
    paywallModalHeaderText: PropTypes.string.tag({
      defaultValue: 'Subscribe to Keep Reading.',
      name: 'Header',
    }),
    paywallModalSubheaderText: PropTypes.string.tag({
      defaultValue: 'You do not have any free stories left this month.',
      name: 'Subheader',
    }),
    paywallModalDescriptionText: PropTypes.string.tag({
      defaultValue: 'Enjoy unlimited access with your subscription.',
      name: 'Offer Description',
    }),
    paywallModalOfferText: PropTypes.string.tag({
      defaultValue: 'Upgrade',
      name: 'Link to Offer Text',
    }),
    paywallModalAllOffersText: PropTypes.string.tag({
      defaultValue: 'See all subscription options.',
      name: 'Link to All Offers Text',
    }),
    paywallModalSignInText: PropTypes.string.tag({
      defaultValue: 'Sign In.',
      name: 'Link to Sign In Text',
    }),
    paywallModalSignInMessageText: PropTypes.string.tag({
      defaultValue: 'I have an account. ',
      name: 'I Have An Account Text',
    }),
    paywallModalOfferUrl: PropTypes.string.tag({
      defaultValue: '/',
      name: 'URL To Offer Page',
    }),
    paywallModalAllOffersUrl: PropTypes.string.tag({
      defaultValue: '/',
      name: 'URL To ALL Offers Page',
    }),
    paywallModalSignInUrl: PropTypes.string.tag({
      defaultValue: '/',
      name: 'URL To Sign In Page',
    }),
    paywallIconButtonUrl: PropTypes.string.tag({
      defaultValue: '/',
      name: 'Close Button Redirect URL',
    }),
    paywallModalSignUpMessageText: PropTypes.string.tag({
      defaultValue: 'Your first time here? ',
      name: 'Sign Up Text',
    }),
    paywallModalSignUpText: PropTypes.string.tag({
      defaultValue: 'Create an Account.',
      name: 'Sign Up Link Text',
    }),
    paywallModalSignUpUrl: PropTypes.string.tag({
      defaultValue: '/subscription#register',
      name: 'Sign Up URL',
    }),
  }),
};

export default Paywall;

Form Elements

Element Description
addressContainer Contains all the fields needed to collect address information from a users
alreadyASubscriber Link for users to click on if they are already a subscriber
button A baseline (empty) button that can be extended or customized
buttonOptions A group of up to 3 buttons that display different options
cartEmpty A component to display an empty cart
cartItem Displays a single item in a cart
cartSummary Displays a summary of the items in the cart
checkbox A single form input of type checkbox
checkoutButton A button for checking out your cart
cityInput An address field for the city a user lives in
confirmPasswordInput A password input field for confirming your password
countryDropdown A dropdown address box for selecting the country a user lives in
displayNameInput An input field for entering a user's display name
DOBInput A input field for entering a user's date of birth (accepts US or EU date formats)
dropdown A baseline (empty) dropdown that can be extended or customized
emailInput An input field for entering the user's an email address
firstAddressInput An address input field for entering the first line of a user's address
firstNameInput An input field for entering the user's first name
forgotPasswordLink A link to direct users to the lost password form
GenderRadioButtons Radio buttons for a user to select their gender preference
getOfferButton A button to accept a campaign offer/product
header Creates a custom h# header
headlineImage A responsive image that is intended for use on the top of the campaign page or for a large image
iconButton A button with an SVG icon in it
input A baseline (empty) input field that can be extended or customized
keepReadingButton A button to keep reading from where the user left off
lastNameInput An input for entering a user's last name
lazyLoadingImage Loads an image using a lazy loader
manageSubscriptionsButton A button you can click to manage your subscription
manageSubStatusBar A status bar for managing subscriptions
modal A modal popup window
notLoggedIn A message to show when the user is not logged in
passwordInput An input field that accepts a user's password
phoneNumberInput An input field that accepts a user's phone number
postalCodeInput An address form field for entering a user's postal code
productCard A card for displaying information about a subscription or product
productCardsContainer A container that wraps all the product cards
productPriceRadioButtons Radio button options specifically for listing prices of a subscription or product
radioButton A baseline radio button input field that can be customized or extended
[redirectLink](https://github.com/wapopartners/core-components/tree/master/packages/feature_subscriptions/src/_children/formElements/redir