async forceStripeSubscriptionToProduct(data, options) {
        if (!this._stripeAPIService.configured) {
            throw new DataImportError({
                message: tpl(messages.noStripeConnection, {action: 'force subscription to product'})
            });
        }

        // Retrieve customer's existing subscription information
        const stripeCustomer = await this._stripeAPIService.getCustomer(data.customer_id);

        // Subscription can only be forced if the customer exists
        if (!stripeCustomer) {
            throw new DataImportError({message: tpl(messages.forceNoCustomer)});
        }

        // Subscription can only be forced if the customer has an existing subscription
        if (stripeCustomer.subscriptions.data.length === 0) {
            throw new DataImportError({message: tpl(messages.forceNoExistingSubscription)});
        }

        // Subscription can only be forced if the customer does not have multiple subscriptions
        if (stripeCustomer.subscriptions.data.length > 1) {
            throw new DataImportError({message: tpl(messages.forceTooManySubscriptions)});
        }

        const stripeSubscription = stripeCustomer.subscriptions.data[0];

        // Subscription can only be forced if the existing subscription does not have multiple items
        if (stripeSubscription.items.data.length > 1) {
            throw new DataImportError({message: tpl(messages.forceTooManySubscriptionItems)});
        }

        const stripeSubscriptionItem = stripeSubscription.items.data[0];
        const stripeSubscriptionItemPrice = stripeSubscriptionItem.price;
        const stripeSubscriptionItemPriceCurrency = stripeSubscriptionItemPrice.currency;
        const stripeSubscriptionItemPriceAmount = stripeSubscriptionItemPrice.unit_amount;
        const stripeSubscriptionItemPriceType = stripeSubscriptionItemPrice.type;
        const stripeSubscriptionItemPriceInterval = stripeSubscriptionItemPrice.recurring?.interval || null;

        // Subscription can only be forced if the existing subscription has a recurring interval
        if (!stripeSubscriptionItemPriceInterval) {
            throw new DataImportError({message: tpl(messages.forceExistingSubscriptionNotRecurring)});
        }

        // Retrieve Ghost product
        let ghostProduct = await this._productRepository.get(
            {id: data.product_id},
            {...options, withRelated: ['stripePrices', 'stripeProducts']}
        );

        if (!ghostProduct) {
            throw new DataImportError({message: tpl(messages.productNotFound, {id: data.product_id})});
        }

        // If there is not a Stripe product associated with the Ghost product, ensure one is created before continuing
        if (!ghostProduct.related('stripeProducts').first()) {
            // Even though we are not updating any information on the product, calling `ProductRepository.update`
            // will ensure that the product gets created in Stripe
            ghostProduct = await this._productRepository.update({
                id: data.product_id,
                name: ghostProduct.get('name'),
                // Providing the pricing details will ensure the relevant prices for the Ghost product are created
                // on the Stripe product
                monthly_price: {
                    amount: ghostProduct.get('monthly_price'),
                    currency: ghostProduct.get('currency')
                },
                yearly_price: {
                    amount: ghostProduct.get('yearly_price'),
                    currency: ghostProduct.get('currency')
                }
            }, options);
        }

        // Find price on Ghost product matching stripe subscription item price details
        const ghostProductPrice = ghostProduct.related('stripePrices').find((price) => {
            return price.get('currency') === stripeSubscriptionItemPriceCurrency &&
                price.get('amount') === stripeSubscriptionItemPriceAmount &&
                price.get('type') === stripeSubscriptionItemPriceType &&
                price.get('interval') === stripeSubscriptionItemPriceInterval;
        });

        let stripePriceId;
        let isNewStripePrice = false;

        if (!ghostProductPrice) {
            // If there is not a matching price, create one on the associated Stripe product using the existing
            // subscription item price details and update the stripe subscription to use it
            const stripeProduct = ghostProduct.related('stripeProducts').first();

            const newStripePrice = await this._stripeAPIService.createPrice({
                product: stripeProduct.get('stripe_product_id'),
                active: true,
                nickname: stripeSubscriptionItemPriceInterval === 'month' ? 'Monthly' : 'Yearly',
                currency: stripeSubscriptionItemPriceCurrency,
                amount: stripeSubscriptionItemPriceAmount,
                type: stripeSubscriptionItemPriceType,
                interval: stripeSubscriptionItemPriceInterval
            });

            await this._stripeAPIService.updateSubscriptionItemPrice(
                stripeSubscription.id,
                stripeSubscriptionItem.id,
                newStripePrice.id,
                {prorationBehavior: 'none'}
            );

            stripePriceId = newStripePrice.id;
            isNewStripePrice = true;
        } else {
            // If there is a matching price, and the subscription is not already using it,
            // update the subscription to use it
            stripePriceId = ghostProductPrice.get('stripe_price_id');

            if (stripeSubscriptionItem.price.id !== stripePriceId) {
                await this._stripeAPIService.updateSubscriptionItemPrice(
                    stripeSubscription.id,
                    stripeSubscriptionItem.id,
                    stripePriceId,
                    {prorationBehavior: 'none'}
                );
            }
        }

        // If there is a matching price, and the subscription is already using it, nothing else needs to be done

        return {
            stripePriceId,
            isNewStripePrice
        };
    }
Force a Stripe customer to be subscribed to a specific Ghost product

This will either:

Create a new price on the Stripe product that is associated with the Ghost product, then update
the customer's Stripe subscription to use the new price. The new price will be created with the details of the
existing price of the item in customer's Stripe subscription

or

Update the customer's stripe subscription to use an existing price on the Stripe product that matches the
details of the existing price of the item in customer's Stripe subscription

If there is no Stripe product associated with the Ghost product, one will be created

This method should be used in-conjunction with `MembersRepository.linkSubscription` to ensure
that the changes made in Stripe are reflected in Ghost - This is not executed as part of this to allow for
flexibility and reduce duplication

@param {Object} data
@param {String} data.customer_id - Stripe customer ID
@param {String} data.product_id - Ghost product ID
@param {Object} options
@returns {Promise<Object>}