yarn add @pinelab/vendure-plugin-stripe-subscription
to install this plugin.
yarn add @pinelab/vendure-plugin-stripe-subscription
to install this plugin.
A channel aware plugin that allows you to sell subscription based services or products through Vendure. This plugin was made in collaboration with the great people at isoutfitters.com.
The default strategy defines subscriptions in the following manner:
You can easily define your own subscriptions with a custom subscription strategy.
vendure-config.ts
plugins and admin UI compilation:import { StripeSubscriptionPlugin } from '@pinelab/vendure-plugin-stripe-subscription';
plugins: [
StripeSubscriptionPlugin.init({
vendureHost: process.env.VENDURE_HOST!,
}),
AdminUiPlugin.init({
port: 3002,
route: 'admin',
app: compileUiExtensions({
outputPath: path.join(__dirname, '__admin-ui'),
extensions: [StripeSubscriptionPlugin.ui],
}),
}),
];
Stripe Subscription
as handlerAPI key
. Publishable key
and Webhook secret
can be left empty at first.Webhook secret
field should now have a value. This means webhooks have been created in your Stripe account. If not, check the server logs. previewStripeSubscriptions(productVariantId: 1) {
name
amountDueNow
variantId
priceIncludesTax
recurring {
amount
interval
intervalCount
startDate
endDate
}
}
previewStripeSubscriptionsForProduct
AddItemToOrder
mutation.activeOrder {
id
code
lines {
stripeSubscriptions {
name
amountDueNow
variantId
priceIncludesTax
recurring {
amount
interval
intervalCount
startDate
endDate
}
}
}
}
createStripeSubscriptionIntent
to receive a client secret.PaymentIntent
or a SetupIntent
.It's important to inform your customers what you will be billing them in the future: https://stripe.com/docs/payments/setup-intents#mandates
You can optionally supply your publishable key in your payment method handler, so that you can retrieve it using the eligiblePaymentMethods
query:
eligiblePaymentMethods {
id
name
stripeSubscriptionPublishableKey
}
You can define your own subscriptions by implementing the StripeSubscriptionStrategy
:
import { SubscriptionStrategy } from '@pinelab/vendure-plugin-stripe-subscription';
import { RequestContext, Injector, ProductVariant, Order } from '@vendure/core';
/**
* This example creates a subscription that charges the customer the price of the variant, every 4 weeks
*/
export class MySubscriptionStrategy implements SubscriptionStrategy {
isSubscription(
ctx: RequestContext,
variant: ProductVariant,
injector: Injector
): boolean {
// This example treats all products as subscriptions
return true;
}
defineSubscription(
ctx: RequestContext,
injector: Injector,
productVariant: ProductVariant,
order: Order,
orderLineCustomFields: { [key: string]: any },
quantity: number
): Subscription {
return {
name: `Subscription ${productVariant.name}`,
priceIncludesTax: productVariant.listPriceIncludesTax,
amountDueNow: productVariant.listPrice,
recurring: {
amount: productVariant.listPrice,
interval: 'week',
intervalCount: 4,
startDate: new Date(),
},
};
}
// This is used to preview the subscription in the storefront, without adding them to cart
previewSubscription(
ctx: RequestContext,
injector: Injector,
// Custom inputs can be passed into the preview method via the storefront
customInputs: any,
productVariant: ProductVariant
): Subscription {
return {
name: `Subscription ${productVariant.name}`,
priceIncludesTax: productVariant.listPriceIncludesTax,
amountDueNow: productVariant.listPrice,
recurring: {
amount: productVariant.listPrice,
interval: 'week',
intervalCount: 4,
startDate: new Date(),
},
};
}
}
You can then pass the strategy into the plugin during initialization in vendure-config.ts
:
StripeSubscriptionPlugin.init({
vendureHost: process.env.VENDURE_HOST!,
subscriptionStrategy: new MySubscriptionStrategy(),
}),
You can pass custom inputs to your strategy, to change how a subscription is defined, for example by having a selectable start date:
subscriptionStartDate
subscriptionStartDate
to your strategy: previewStripeSubscriptionsForProduct(
productVariantId: 1
customInputs: { subscriptionStartDate: "2024-01-01" }
) {
name
amountDueNow
variantId
priceIncludesTax
recurring {
amount
interval
intervalCount
startDate
endDate
}
}
previewSubscription(
ctx: RequestContext,
injector: Injector,
customInputs: { subscriptionStartDate: string },
productVariant: ProductVariant
): Subscription {
return {
name: `Subscription ${productVariant.name}`,
priceIncludesTax: productVariant.listPriceIncludesTax,
amountDueNow: productVariant.listPrice,
recurring: {
amount: productVariant.listPrice,
interval: 'week',
intervalCount: 4,
startDate: new Date(customInputs.subscriptionStartDate),
},
};
}
subscriptionStartDate
on the order line, so that you can access it in the defineSubscription
method of your strategy: defineSubscription(
ctx: RequestContext,
injector: Injector,
productVariant: ProductVariant,
order: Order,
orderLineCustomFields: { [key: string]: any },
quantity: number
): Subscription {
return {
name: `Subscription ${productVariant.name}`,
priceIncludesTax: productVariant.listPriceIncludesTax,
amountDueNow: productVariant.listPrice,
recurring: {
amount: productVariant.listPrice,
interval: 'week',
intervalCount: 4,
startDate: new Date(orderLineCustomFields.subscriptionStartDate),
},
};
}
It's possible to define multiple subscriptions per product. For example when you want to support down payments or yearly contributions.
Example: A customer pays $90 a month, but is also required to pay a yearly fee of $150:
defineSubscription(
ctx: RequestContext,
injector: Injector,
productVariant: ProductVariant,
): Subscription {
return [
{
name: `Monthly fee`,
priceIncludesTax: productVariant.listPriceIncludesTax,
amountDueNow: 0,
recurring: {
amount: 9000,
interval: 'month',
intervalCount: 1,
startDate: new Date(),
},
}, {
name: `yearly fee`,
priceIncludesTax: productVariant.listPriceIncludesTax,
amountDueNow: 0,
recurring: {
amount: 15000,
interval: 'year',
intervalCount: 1,
startDate: new Date(),
},
}
];
}
OrderItemCalculationStrategy
. The strategy in this plugin is used for calculating the
amount due for a subscription, if the variant is a subscription. For non-subscription variants, Vendure's default
order line calculation is used. Only 1 strategy can be used per Vendure instance, so any other
OrderItemCalculationStrategies are overwritten by this plugin.You can cancel a subscription by canceling the corresponding order line of an order. The subscription will be canceled before the next billing cycle using Stripe's cancel_at_period_end
parameter.
Only initial payments of subscriptions can be refunded. Any future payments should be refunded via the Stripe dashboard.
You can use the payment eligibility checker has-stripe-subscription-products-checker
if you to use a different payment method for orders without subscriptions. The has-stripe-subscription-products-checker
makes your payment method not eligible if it does not contain any subscription products.
The checker is added automatically, you can just select it via the Admin UI when creating or updating a payment method.
You can locally test this plugin by checking out the source.
STRIPE_APIKEY=sk_test_****
STRIPE_PUBLISHABLE_KEY=pk_test_****
VENDURE_HOST=https://280n-dn27839.ngrok-free.app
yarn start
http://localhost:3050/checkout
to view the Stripe checkoutPaymentSettled
in the admin.