digitallyinduced / ihp

🔥 The fastest way to build type safe web apps. IHP is a new batteries-included web framework optimized for longterm productivity and programmer happiness
https://ihp.digitallyinduced.com/
MIT License
4.93k stars 195 forks source link

Some issues on the Stripe Docs #1315

Closed kodeFant closed 2 years ago

kodeFant commented 2 years ago

Hi!

I am using the Stripe plugin in IHP Pro for the first time, and there seems to be some issues, so collecting them here and in the process rubber ducking what I should do to fix it for myself :)

1: Trailing comma in plans schema

CREATE TABLE plans (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    name TEXT NOT NULL,
    stripe_price_id TEXT NOT NULL, <--
);

Not a biggie, it just fails when pasting this into Schema.sql :)

2: Web/Controller/CheckoutSessions.hs file

a

In the CheckoutSuccessAction, we fetch #planId from the user.

plan <- fetchOne (get #planId currentUser)

A bit further up in the Guide, the steps to add this column should maybe also mention plan_id and subscription_id as it's used in the tutorial.

CREATE TABLE users (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    -- ...,

    stripe_customer_id TEXT DEFAULT NULL
);

BTW, is it right that both the users and the subscriptions table should reference planId or should it be enough with one of them?

Oh, yeah, this is where I first discovered #1314 where I try to add that field through the schema editor and it wipes my Schema.sql so I have to start fresh 😄

b

References planId here, but id doesn't exist. Should probably be get #id plan?

             , metadata =
                    [ ("userId", tshow currentUserId)
                    , ("planId", tshow planId)
                    ]

c

Also, the file is redirecting to SwitchToProAction and PricingAction that aren't part of the tutorial. Might be confusing to beginners. Maybe just put redirectToPath "/"here?

3: Confusing mentions of SubscriptionController

Just below the CheckoutSessions.hs, there is an instruction to add

instance AutoRoute SubscriptionsController

This controller has not been mentioned anywhere before. I assume this is an error and should be CheckoutSessionsController.

4: Stripe Tax being mandatory is troublesome for a new business in Norway

Wohoo :grinning:! This seems to have been due to my Stripe settings, so was not an issue afterall :tada:

5: /Web/Controller/StripeWebhook.hs

at

newRecord @Subscription

there is an issue of ambiguity:

Ambiguous occurrence ‘Subscription’
It could refer to
   either ‘Web.Controller.Prelude.Subscription’,
          imported from ‘Web.Controller.Prelude’ at /home/lillo/kode/volve-chat/Web/Controller/StripeWebhook.hs:3:1-29
          (and originally defined in ‘Generated.Types’
             at /home/lillo/kode/volve-chat/build/Generated/Types.hs:369:1-61)
       or ‘IHP.Stripe.Actions.Subscription’,
          imported from ‘IHP.Stripe.Actions’ at /home/lillo/kode/volve-chat/Web/Controller/StripeWebhook.hs:5:1-25typecheck

I fixed the issue by changing it to @Web.Controller.Prelude.Subscription.

Then, there is an issue here with #stripeSubscriptionId as the guide hasn't mentioned that this also should be included in the user schema:

                user
                    |> setJust #stripeSubscriptionId (get #id subscription)
                    |> setJust #planId planId
                    |> setJust #stripeCustomerId customer
                    |> updateRecord

Added a subscription_id column and changed to |> setJust #subscriptionId (get #id subscription) to make it compile.

6: OpenBillingPortalAction

in order for this to work:

        subscription <-
            currentUser
                |> get #subscriptionId
                |> fetch

        stripeSubscription <- Stripe.send Stripe.RetrieveSubscription{id = get #stripeSubscriptionId subscription}

fetchOne must be used instead of fetch to compile.

It was easy to deduce from previous instructions, but might be worth mentioning that these imports should be added:

import qualified IHP.Stripe.Actions as Stripe
import qualified IHP.Stripe.Types as Stripe

7: Handle Canceling subscriptions?

Right now, it's great that we have access to the Billing Portal because there we could have the user simply cancel the subscription. I currently can't find any actions to perform when the customer has cancelled.

Could we maybe get a Cancel action for the StripeEventController? Or a way to add our own actions to the Stripe Webhook?

kodeFant commented 2 years ago

Now I think I have a working Stripe integration. Thanks for all the great work on this so far :) Hope some of this feedback is helpful!

kodeFant commented 2 years ago

BTW, I developed functionality of handling cancellation and trial ending on MainQuest, where you helped me with "from scratch" code about a year ago.

I think you still have access to that repo @mpscholten , so feel free to look there and take from it if you haven't solved this already.

Added these cases to StripeEvent that let me handle the whole customer lifecycle automatically:

    | CustomerSubscriptionDeleted
    | CustomerSubscriptionUpdated
    | CustomerSubscriptionTrialWillEnd

To me, these events would be necessary when using the Stripe plugin, so might stick to the "from scratch approach" until then. Or at least handling of the webhook

mpscholten commented 2 years ago

Thanks for the feedback! 👍

Just pushed changes for all feedback from 1 to 6.

BTW, is it right that both the users and the subscriptions table should reference planId or should it be enough with one of them?

Yes that's right. A user could have multiple subscriptions with different plans over time: subscribe for a month, then unsubscribe for a month, then subscribe again. Now the user would have two subscription records. The plan_id on the users table should always point to the active plan, while the plan_id in the subscriptions table always points to the plan at that point in time.

Oh, yeah, this is where I first discovered #1314 where I try to add that field through the schema editor and it wipes my Schema.sql so I have to start fresh 😄

Sorry again for that bug 😅

Also, the file is redirecting to SwitchToProAction and PricingAction that aren't part of the tutorial. Might be confusing to beginners. Maybe just put redirectToPath "/"here?

👍 done

7: Handle Canceling subscriptions?

this has been requested already and was actually planned already for v0.17 but then didn't make it in time :)

I think you still have access to that repo @mpscholten , so feel free to look there and take from it if you haven't solved this already.

Couldn't find the repo anymore. Do you have a link?

Happy to add that cancel functionality quickly 👍

kodeFant commented 2 years ago

Thanks Marc, you're the most efficient maintainer I have ever known :+1: I actually just made an implementation on a fresh project I am happy about. I'll send you snippets on Slack :smiley:

stephenbenedict commented 2 years ago

+1 regarding cancel functionality. Even just a webhook event for deleted subscriptions would be enough for my needs (though of course cancellation built into the API would be great).

mpscholten commented 2 years ago

@stephenbenedict 7 is also already done actually with the help of @kodeFant :) Check the docs here

If you upgrade to a recent build here https://ihp.digitallyinduced.com/Builds you should be able to use it 👍

stephenbenedict commented 2 years ago

Thanks a lot! Will look into that right away.