Subscriptions
With the shop you're able to provide a subscription system for frontend user using Stripe.
Defining Subscriptions
- Log-in on dashboard.stripe.com and switch to the Products section. Here you need to create a product for each subscription you offer - for example Basic-Plan and Premium-Plan. This products needs the following data:
- Name: Product names are visible to customers at checkout, in receipts, invoices, and the customer portal.
- Description: Product descriptions appear at checkout, in the customer portal, and on quotes.
- Insert a price and ensure Recurring payment is selected.
- Add a meta tag with th key
category
to the product. Products can be filtered by the value of this tag inSubscriptionTable
plugin (See section "Display table of subscription products"). - Add a meta tag to the product with the key
usergroups
. Set the value of this tag to a comma-separated list of frontend user group ids. These user groups are automatically added to the frontend user who purchases a subscription that includes this product. When a subscription ends, these user groups are automatically removed from the frontend user. - Configure webhooks (See section "Configure stripe webhooks").
Attention:
When using Stripe customers the frontend user needs the short iso-code of his country in his user record (for example: DE)!
Configure stripe webhooks
In order for the user groups associated with the Stripe product to be automatically assigned to the user / automatically removed from the user, a webhook must be set up to receive Stripe events.
For ease of configuration, a Short URL can be set up for the page with a SubscriptionTable
or Subscriptions
plugin.
ShopSubscriptionsPlugin:
type: Extbase
limitToPages:
- {page uid with the Subscriptions plugin}
extension: ShopPro
plugin: Subscriptions
routes:
- routePath: '/stripe-subscription-callback'
_controller: 'Subscription::stripeSubscriptionCallback'
defaultController: 'Subscription::list'
or
ShopSubscriptionsPlugin:
type: Extbase
limitToPages:
- {page uid with the SubscriptionTable plugin}
extension: ShopPro
plugin: SubscriptionTable
routes:
- routePath: '/stripe-subscription-callback'
_controller: 'Subscription::stripeSubscriptionCallback'
defaultController: 'Subscription::list'
Configuration of the webhook in the Stripe dashboard
- Go to the https://dashboard.stripe.com/webhooks page and click Add Endpoint.
- Enter the URL of the page that was set up in step "Setup of the webhook page".
- Select "Select Events" and select
customer.subscription.created
,customer.subscription.updated
andcustomer.subscription.deleted
. - Click "Add Endpoint".
- Click on the newly created webhook entry and copy the endpoint secret for signature.
- Paste the key into the
subscriptionEndpointSecret
TypoScript constant
PSR-14 Events
If
- the webhook is set up correctly
- one of the supported events arrives at the webhook
- a user exists in the database with the corresponding Stripe-customer id
a PSR-14 event is emitted.
Add the following to Configuration/Services.yaml
to react to the events:
services:
Vendor\MyExtension\EventListener\SubscriptionCreatedEventListener:
tags:
- name: event.listener
identifier: 'SubscriptionCreatedEventListener'
event: CodingMs\ShopPro\Event\Stripe\Subscriptions\StripeSubscriptionCreatedEvent
Vendor\MyExtension\EventListener\SubscriptionUpdatedEventListener:
tags:
- name: event.listener
identifier: 'SubscriptionUpdatedEventListener'
event: CodingMs\ShopPro\Event\Stripe\Subscriptions\StripeSubscriptionUpdatedEvent
Vendor\MyExtension\EventListener\SubscriptionDeletedEventListener:
tags:
- name: event.listener
identifier: 'SubscriptionDeletedEventListener'
event: CodingMs\ShopPro\Event\Stripe\Subscriptions\StripeSubscriptionDeletedEvent
This event contains the Stripe subscription object and the associated frontend user.
You can find more information about registering an event listener here: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Events/EventDispatcher/Index.html#registering-the-event-listener
Display table of subscription products
- use the plugin
SubscriptionTable
- during product creation insert a Metadata information
category
and set a custom identifier (for example: subscription). This allows you to display specific subscription products in your tables.
Display list of subscriptions
- use the plugin
Subscriptions
The user has the possibility to view the list of his active and already expired subscriptions and to cancel the subscriptions immediately or at the end of the subscription period. For each subscription, the user has the possibility to view the list of invoices and download them.
Side note: User sync
Stripe requires to have a Stripe-Customer record for each of our frontend user. For solving this requirement, our frontend user has a field Stripe-Customer ID. When a frontend user is logged-in and the Stripe-Customer ID is emtpy, the Shop tries to create a new Stripe-Customer and inserts the Stipe-Customer ID within the user. If the frontend user address data will be changed, the changes data will be synchronized automatically to the Stripe-Customer.
It is very important to keep the Stripe-Customer is up-to-date when using some third-party tools or scripts for changing frontend user data. If necessary, the user synchronization can be called manually. An example can be found in EXT:shop_pro/Classes/EventListener/AfterProfileUpdateEventListener.php
public function __invoke(AfterProfileUpdatedEvent $event): void
{
$frontendUserRepository = GeneralUtility::makeInstance(FrontendUserRepository::class);
$frontendUser = $frontendUserRepository->findOneByUid($event->getFrontendUser()->getUid() ?? 0);
if(!isset($frontendUser)){
return;
}
$subscriptionServiceSettings = TypoScriptService::getTypoScript(
(int)$GLOBALS['TSFE']->id
)['plugin']['tx_shop']['settings']['basketOrder']['orderOptions']['stripe'];
$subscriptionService = GeneralUtility::makeInstance(SubscriptionService::class, $subscriptionServiceSettings);
$subscriptionService->updateStripeCustomer($frontendUser);
}
Notice:
All Stripe elements will be used in language which the user uses on the website right now. This language is fetched from the site configuration.