Product Offerings
/product-offerings — ProductOfferingsModule, guarded by DocuSignGuard.
The page presents Payability’s product catalog with the supplier’s personal eligibility against each. It is also one of the two pages DocuSign returns to, so a contract signed from here is processed before the page renders.
Availability
ProductOfferingsService.fetchProducts(freeAccess) calls
CrossSellService.fetchAvailability(supplierKey) and turns the response into
offer cards.
fetchProducts(freeAccess: boolean): Observable<ProductOfferingsCommonOfferConfig[]> {
return this.crossSellService.fetchAvailability(this.user.getCurrentUserId()).pipe(
map((data: CrossSellAvailability) => {
this.setData(data);
const response = {};
Object.entries(data).forEach(([key, product]) => {
if ([CrossSellProductEnum.InstantAdvance, CrossSellProductEnum.AccessPlus].includes(key as CrossSellProductEnum)
&& product.status === CrossSellAvailabilityEnum.CannotRequest) {
return; (1)
}
response[key] = productsConfig(this.injector, product, freeAccess)[key];
});
return Object.values(response);
}),
);
}
| 1 | Instant Advance and Access Plus are hidden entirely when they cannot be requested. Seller Card and Instant Transfer are always rendered, with their status shown on the card. |
|
|
The product catalog
product-offerings.config.ts builds one ProductOfferingsCommonOfferConfig per
CrossSellProductEnum member.
| Product | Icon | Sign-up action |
|---|---|---|
|
|
|
|
|
|
|
via |
Model-defined |
|
via |
Model-defined (injector-aware) |
Simple products are plain configuration objects; Instant Advance and Access Plus
are model classes because their card content depends on runtime state.
ProductAccessPlusModel additionally takes the Injector so it can resolve
services while building its configuration.
Free-access copy
Every translation key is chosen at build time based on the freeAccess flag:
subheaderKey: freeAccess
? `${freeAccessProduct(CrossSellProductEnum.SellerCard)}.SUBHEADER`
: `${commonProduct(CrossSellProductEnum.SellerCard)}.SUBHEADER`,
freeAccessProduct() and commonProduct() (product-offerings.utils.ts) build
the namespace prefix. Four keys vary per product: SUBHEADER, DESC,
GENERAL_ELIGIBILITY, GENERAL_PRICING. The flag comes from
selectIsFreeAccess, backed by status.isFreeAccess.
|
Adding a product means adding eight translation keys — four for the common namespace and four for free access. A missing free-access key renders as the raw key string for that cohort only, which is easy to miss in testing. |
Offer modes
export enum ProductOfferingModeEnum {
DEFAULT = 'DEFAULT',
CAN_REFINANCE = 'CAN_REFINANCE',
REFINANCING_PROCESSING = 'REFINANCING_PROCESSING',
}
Refinancing modes change an offer card into a refinance prompt or a "processing" state for suppliers with an existing advance.
Components
|
Page shell; subscribes to |
|
The offer card — header, description, eligibility, pricing, CTA. |
|
A labelled feature row inside a card. |
|
Instant Advance card configuration. |
|
Access Plus card configuration. |
|
Access Plus sign-up behaviour. |
|
Translation-namespace helpers. |
DocuSign integration
DocuSignGuard on this route means a contract signed from a product card returns
here and is processed before render. DocusignModeEnum.ProductOfferingsList
tells DocusignVerificationService.refreshData() to call
ProductOfferingsService.loadProducts() afterwards, so availability reflects the
new contract.
Cross-sell analytics
| Constant | Segment event |
|---|---|
|
|
|
|
|
|
The product_cross_sell_page dashboard notification drives traffic here via
CtaCrossSellStrategy — see
Dashboard Notifications & CTAs.
Onboarding counterpart
views/onboarding/product-offering/ is a separate implementation used during
onboarding, with its own components per product (advance-line/,
instant-advance/, instant-access/, access-plus/, new-free-access/) and a
repayment-slider/. The two are not shared. See
Onboarding.