Discount codes for Stripe, pls

Hi there,

Ghost/Stripe checkout doesn’t currently support discount codes.

These would let folks have a month or two discounted, for free, so they could trial membership. This would lean to more signups.

This is also something that Substack supports, and is very handy.

Ghost does support free trials, documented here:

And you can already use/apply discount codes directly in Stripe, if you want to create special offers. It’s manual, but it works fine - I have quite a few setup myself

1 Like

amazing!!! thanks @John

ps. being able to incorporate coupon codes (not free trials) into the checkout screen would be great.

Stripe are currently beta testing that - I’ve seen it and works well. I imagine they’ll release it relatively soon :slight_smile:

1 Like

Hi John - I’m hoping to do this as well as I have a few thousand members on a different site I’d like to bring over and I have to offer them a discount. It’s a bit tedious to do it manually but if you had a solution for something broader, that would really be cool.

hey Rob, you could always open membership on a public link at a discount, but only tell your members about it and keep it open for, say, 28 days?

That way you can invite them over for the discounted price, and then just switch to the regular price.

Thanks @jsh4 - that was kind of what I was thinking. However… I’ve been playing around a bit with how coupons might work and am happy to do a PR but for the life of me I can’t see where the checkout session is created. I’m doing a full scale search within the project and I’m utterly stumped.

I then got to thinking “why don’t I just create my own checkout” using Stripe Elements. I was thinking that I could apply a coupon there (which I can) but there’s no way, that I can see, to add the member_id to subscription.

Finally - I see how the membership API is being used to create the session then redirect off to Stripe. I can intercept all of it and make my own thing easily - I just want to pass in a coupon ID through the session which the docs say is possible:

It would seem a simple thing to add to the API call… Happy to PR this if I could just get a pointer to where the Stripe Session is created.

sorry, can’t help you here… One for @DavidDarnes perhaps?

OK I think I figured this out. @John weigh in and let me know if I’m crazy :).

I dove into the members-api package and noticed that a metadata key was being passed in and, as far as I can tell, it’s not being used for anything currently. It seems like it’s there for theming reasons? Not sure - anyway I worked up a Vue/axios call to the API endpoint that looks like this:

const sessionRes = await axios.get("/members/api/session");

const payload ={
    plan: "Monthly",
    successUrl: location.href,
    cancelUrl: location.href,
    metadata: {coupon: 'asdasdasd'}
    const stripeCheckoutSession = await"/members/api/create-stripe-checkout-session/", payload);

    //if we're here we have a bad coupon code

I made two small tweaks to the createCheckoutSession method, which were to pull the payload into a variable and then check for the presence of a coupon passed in through the metadata:

const payload = {
    payment_method_types: ['card'],
    success_url: options.successUrl || this._checkoutSuccessUrl,
    cancel_url: options.cancelUrl || this._checkoutCancelUrl,
    customer: customer ? : undefined,
    customer_email: customerEmail,
    subscription_data: {
        trial_from_plan: true,
        items: [{
if(metadata && =;
const session = await this._stripe.checkout.sessions.create(payload);

As you can see, it works great when there’s a valid coupon code :).


The one thing I’m not a super huge fan of is trapping the 400 here - ideally the API would return a wrapper for the checkout with a success/fail flag or the like and an error message the dev could do something with.

I’m happy to PR both things if you find this useful.