remp / crm-salesfunnel-module
CRM SalesFunnel Module
Installs: 35 558
Dependents: 1
Suggesters: 0
Security: 0
Stars: 1
Watchers: 8
Forks: 6
Open Issues: 1
Requires
- php: ^8.1
- dev-master
- 3.5.0
- 3.4.0
- 3.3.1
- 3.3.0
- 3.2.1
- 3.2.0
- 3.1.1
- 3.1.0
- 3.0.1
- 3.0.0
- 2.11.1
- 2.11.0
- 2.10.1
- 2.10.0
- 2.9.1
- 2.9.0
- 2.8.0
- 2.7.0
- 2.6.0
- 2.5.0
- 2.4.0
- 2.3.0
- 2.2.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.0
- 1.2.1
- 1.2.0
- 1.1.0
- 1.0.0
- 1.0.0-beta2
- 1.0.0-beta1
- 0.39.0
- 0.38.0
- 0.37.1
- 0.37.0
- 0.36.0
- 0.35.0
- 0.34.0
- 0.33.0
- 0.32.0
- 0.31.0
- 0.30.0
- 0.29.0
- 0.28.0
- 0.27.0
- 0.26.0
- 0.25.0
- 0.24.0
- 0.23.0
- 0.22.0
- 0.21.0
- 0.20.0
- 0.18.0
- 0.17.0
- 0.16.0
- 0.15.0
- 0.14.0
- 0.13.0
- 0.12.0
- 0.11.0
- 0.10.0
- 0.9.0
- 0.8.2
- 0.8.1
- 0.8.0
- 0.7.0
- 0.6.0
- 0.5.0
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.10
This package is auto-updated.
Last update: 2024-10-25 12:18:30 UTC
README
Installing module
We recommend using Composer for installation and update management.
composer require remp/crm-salesfunnel-module
Enabling module
Add installed extension to your app/config/config.neon
file.
extensions: - Crm\SalesFunnelModule\DI\SalesFunnelModuleExtension
Configuration
By default we register for every sales funnel short route (e.g. for sales funnel with URL key sample
, route is http://crm.press/sample
). You can turn off registration of slug by adding following setting to configuration file.
sales_funnel: funnel_routes: false
Sales funnel will be still accessible through long route (e.g. http://crm.press/sales-funnel/sales-funnel-frontend/show?funnel=sample
).
Scheduled commands
Please add following commands to your scheduler:
sales-funnel:distributions
. Every sales funnel precalculates data about users who paid through it in advance. These calculations are used to provide stats and distributions on the funnel detail. Stats are updated on the fly, but it's recommended to run this once a day.
Using sales funnels
Sales Funnel module provides a way to present subscription type offering to user in a way that's completely independent from the rest of application.
You can find list of available sales funnels and create new funnels in CRM admin (/sales-funnel/sales-funnels-admin/
).
To create new sales funnel, you need to enter several fields (required are bold):
- Name. Name of the sales funnel displayed in CRM admin listings and statistics.
- Active. Flag, whether the window should be available to users or not.
- URL key. Slug under which the window is available. All funnels are available at custom routes - e.g. sample funnel with url key
sample
would be available athttp://crm.press/sales-funnel/sales-funnel-frontend/show?funnel=sample
and athttp://crm.press/sample
(if slugs are allowed, see Configuration). - Only logged. Flag to limit access to only logged in users.
- Only not logged. Flag to limit access to only not logged in users.
- Segment. Segment selection to limit access only to members of a specific segment.
- Valid from. Date constraint allowing funnel to be available only after specified date.
- Valid to. Date constraint allowing funnel to be available only before specified date.
- Funnel HTML content. HTML content of funnel (top-bottom HTML needs to be provided; description below)
- Funnel no access HTML. HTML to display to user if she doesn't have an access to window due to specified constraints.
If no HTML is provided, default
funnel-no-access
is displayed - Funnel error screen HTML. HTML to display if there's any kind of validation error with funnel.
If no HTML is provided, default
funnel-error
is displayed
Once the funnel is created, there are more options that can be configured within the detail of created funnel:
-
Subscription types. Every funnel has list of whitelisted subscription types. Array of allowed subscription types is provided to HTML content template as a twig variable for further usage. Only whitelisted subscription types will be allowed to be submitted within funnel, otherwise the validation will return an error.
-
Payment gateways. Every funnel has a list of whitelisted payment gateways. Array of allowed gateways is provided to HTML content template as a twig variable for further usage. Only whitelisted gateways will be allowed to be submitted within funnel, otherwise the validation will return an error.
The HTML content of funnel can be anything from very simple HTML form to micro JS application calling backend APIs and routing user through multi-step experience. Funnels support Twig templating and provide variables to be used within the template:
-
funnel
: Instance ofsales_funnels
table row (as anNette\DB\Table\ActiveRow
implementation) -
isLogged
: Flag, whether the user is logged in or not. Should be utilized to display login form. -
gateways
: Array ofgateways
table rows. Should be utilized to show/hide whitelisted gateways in combination with Twig'sif
statements:{% if gateways['paypal'] is defined %} <div class="row"><!-- gateway input --></div> {% endif %}
-
subscriptionTypes
: Array ofsubscription_types
table rows. Should be utilized to show/hide whitelisted subscription types in combination with Twig'sif
statements:{% if subscriptionTypes['web_month'] is defined %} <div class="row"><!-- subscription type input --></div> {% endif %}
-
addresses
: Array with all stored user's addresses -
meta
: Sales funnel metadata seeded into thesales_funnel_meta
table -
jsDomain
: Javascript domain to be used if working with cookies -
actualUserSubscription
: Reference to actual user's subscription - row ofsubscriptions
table as anNette\DB\Table\ActiveRow
implementation. -
referer
: Referer parameter used to access the sales funnel. Loaded primarily fromreferer
query string parameter. If not provided, HTTP referer is used instead. -
values
: Array of values submitted before if the page is reloaded. -
errors
: Array of errors generated on last submit.
You can use/ignore any of these variables. They're provided to give your template information about user for you to decide what user should see.
Twig also provides a translation filter trans
which can be used to translate strings within the template or encapsulate duplicated texts or messages across the funnels:
<label>{{ 'internal.salesfunnel.gateway.cardpay.label'|trans }}</label> {# This will work if you're using an Internal module with the existing translation key 'internal.salesfunnel.gateway.cardpay.label' in your translations #}
The final output of sales funnel should be POST request to /sales-funnel/sales-funnel-frontend/submit
- either via
HTML form or via AJAX request. The POST params should contain:
funnel_url_key
: URL key of funnel defined when creating sales funnel. Can be populated from{{ funnel.url_key }}
variable.auth
: Flag0
/1
whether CRM should attempt to authenticate user with email and password.email
: If authenticating, user should provide his email.password
: If authenticating, user should provide his password.subscription_type
: Code of subscription type (not ID) that user selected to purchase.payment_gateway
: Code of payment gateway user selected to pay through.additional_amount
: Additional amount of money that should be included within the payment on top of standard subscription type price. Used for donations.additional_type
: Flag, whether theadditional_amount
should be included in the future charges if the payment is made via recurrent gateway. Allowed values aresingle
/recurrent
.address_id
: If user was offered an address in sales funnel, you can submit ID of address that will be stored within the payment and later in created subscription. Useful if you want to enforce delivery address to be non-standard.custom
: Key-value pairs (i.e.custom[key]=value
) which will be stored as text in payment's note field.allow_redirect
: Flag specifying whether the response should contain redirect headers or just JSON response with URL of payment gateway. Useful if you don't want to redirect user away from CRM, but only submit values via AJAX and display payment gateway within iframe of a modal window.
If the form is valid, backend will create new unfinished payment for the user and the server response will contain redirection to payment gateway.
Sample funnel
Module provides seeder with single demo funnel which contain very barebone bootstrap-based design and simple javascript handler to ask and validate user's email/password.
When the module is enabled, you can seed this funnel by running php bin/command.php application:seed
and it will get
available at /sample
URL on your domain. You can (and should) also find your funnel in CRM admin
(/sales-funnel/sales-funnels-admin/)
as you still need to enable subscription types and gateways for this newly seeded
funnel to work correctly.
If you want to only view the sample implementation of funnel, you can find it here.
Events and handlers
PaymentItemContainerReadyEvent
This event is emitted from SalesFunnelFrontedPresenter::renderSubmit
after PaymentItemContainer
was initialized and filled with base payment items (eg SubscriptionTypePaymentItem
) but before payment is created.
All handlers which register listener for this event have access to whole PaymentItemContainer
. It can be used to add payment items before payment is created and before customer is redirected to payment provider.
ExampleModule implementation
For example - your sales funnel contains specific donation input field with name specific_donation
and you want to add it to the payment.
Create handler Crm\ExampleModule\Events\PaymentItemContainerReadyEventHandler
:
<?php namespace Crm\ExampleModule\Events; use Crm\PaymentsModule\PaymentItem\DonationPaymentItem; use League\Event\AbstractListener; use League\Event\EventInterface; class PaymentItemContainerReadyEventHandler extends AbstractListener { public function handle(EventInterface $event) { $paymentData = $event->getPaymentData(); if (isset($paymentData['specific_donation'])) { $paymentItemContainer = $event->getPaymentItemContainer(); $paymentItemContainer->addItem( new DonationPaymentItem( $name = 'Specific donation', $price = $paymentData['specific_donation'], $vat = 0 ) ); } } }
And initialize listener in ExampleModule\ExampleModule.php
public function registerEventHandlers(\League\Event\Emitter $emitter) { //... $emitter->addListener( \Crm\SalesFunnelModule\Events\PaymentItemContainerReadyEvent::class, $this->getInstance(\Crm\ExampleModule\Events\PaymentItemContainerReadyEventHandler::class) ); //...
Newly created payment by SalesFunnel will now contain specific donation if sales funnel received this field.
Sales funnel twig variables and snippets
Registration
When using sales funnels you can also pass custom variables or use snippets feature provided by ApplicationModule Crm\ApplicationModule\Repositories\SnippetsRepository
.
For this feature to work you need to register custom data provider which implements SalesFunnelVariablesDataProviderInterface
in sales_funnel.dataprovider.twig_variables
data provider path.
registration:
$dataProviderManager->registerDataProvider( 'sales_funnel.dataprovider.twig_variables', $this->getInstance(SalesFunnelTwigVariablesDataProvider::class) );
data provider example:
final class SalesFunnelTwigVariablesDataProvider implements SalesFunnelVariablesDataProviderInterface { // add ability to load snippets, check docblock for further info use SalesFunnelTwigSnippetLoaderTrait; public function provide(array $params): array { if (!isset($params[self::PARAM_SALES_FUNNEL])) { throw new DataProviderException('missing [' . self::PARAM_SALES_FUNNEL . '] within data provider params'); } $salesFunnel = $params[self::PARAM_SALES_FUNNEL]; $returnParams = []; // adding custom twig variable $returnParams['your_custom_twig_variable'] = 'your_custom_twig_variable'; // load snippets (feature provided by ApplicationModule) $snippetsToLoad = ['header', 'footer', 'sales-funnel-common-header']; $returnParams += $this->loadSnippets($salesFunnel, $loadSnippets); return $returnParams; } }
Usage within sales funnels
Snippet's name is changed to camel case with prefix snippet. So sales-funnel-common-header
becomes:
{{ snippetSalesFunnelCommonHeader|raw }}
Variables are accessible as provided. So your_custom_twig_variable
will be:
{% if your_custom_twig_variable %} <h1>{{ your_custom_twig_variable }}</h1> {% endif %}
Iframe deprecation in sales funnels
Deprecation
Because of modernization and multiple problems with using iframes as wrapper for sales funnels (eg. scrolling & focus issues on iOS devices), we stopped using iframe to render sales funnels.
Methods / routes that are now marked deprecated:
Crm\SubscriptionsModule\Presenters\SubscriptionsPresenter#renderNew()
- Serves URLs http://crm.press/subscriptions/subscriptions/new
Crm\SalesFunnelModule\Presenters\SalesFunnelFrontendPresenter#renderDefault()
⚠️ They will be removed (or refactored) in CRM version 4.0.
Solution
This will be default solution
If you are using Subscriptions:Subscriptions:new
as default route, we recommend to update your config Default route
(see Application category at https://crm.press/admin/config-admin/) to SalesFunnel:SalesFunnel:newPopup
. This method loads and renders default sales funnel from config Default sales funnel
(see Sales Funnels category) directly without using iframes.
You can register routes for your sales funnels directly in your custom module. For example:
use Crm\SalesFunnelModule\DI\Config; use Crm\SalesFunnelModule\Models\SalesFunnelsCache; class DemoModule extends CrmModule { // ... public function registerRoutes(RouteList $router) { // to avoid iframes for all sales funnels of Demo instance if ($this->config->getFunnelRoutes()) { foreach ($this->salesFunnelsCache->all() as $salesFunnel) { $router->prepend(new Route( "<funnel {$salesFunnel->url_key}>", 'SalesFunnel:SalesFunnelFrontend:show' )); } } } // ... }
For commonly used parts of sales funnel such as header, footer etc., you can use snippets feature provided by ApplicationModule
.
Components
AmountDistributionWidget
Admin sales funnel detail distribution stats component.
DaysFromLastSubscriptionDistributionWidget
Admin sales funnel detail distribution stats component.
FinishRegistrationWidget
Frontend successful payment/registration widget.
NewSubscriptionWidget
Frontend new subscription widget with iframe containing sales funnel.
PaymentDistributionWidget
Admin sales funnel detail stats widget.
SalesFunnelUserListingWidget
Admin user listing widget.
SubscriptionTypesInSalesFunnelsWidget
Admin sales funnel detail subscription types listing.
WindowPreview
Admin sales funnel detail preview.