Search code examples
graphqlstripe-paymentsstrapi

How to return Stripe coupons using Strapi GraphQL


I've got a Nuxt app with a Checkout page, and on the backend I'm using Strapi GraphQL. I created several coupons in Stripe, and I want to be able to verify the coupons from the Checkout page, but I'm struggling to figure out how to do this. Here's what I have so far:

Frontend (Nuxt)

Cart.vue:

this.$apollo.query({
  query: validateCouponQuery,
  variables: {
    coupon: this.coupon
  }
})

validateCoupon.gql:

query($coupon: String!) {
  validateCoupon(coupon: $coupon) {
    id
    name
    valid
  }
}

Backend (Strapi): ./order/config/routes.json:

{
  "method": "GET",
  "path": "/orders/validateCoupon",
  "handler": "order.validateCoupon",
  "config": {
    "policies": []
  }
}

./order/config/schema.graphql.js:

const { sanitizeEntity } = require('strapi-utils');

module.exports = {
  query: `
    validateCoupon(coupon: String): Order
  `,
  resolver: {
    Query: {
      validateCoupon: {
        resolverOf: 'Order.validateCoupon',
        async resolver(_, { coupon }) {
          const entity = await strapi.services.order.validateCoupon({ coupon });
          return sanitizeEntity(entity, { model: strapi.models.order });
        }
      }
    }
  }
}

./order/controllers/order.js:

'use strict';
require('dotenv').config();

const stripe = require('stripe')(`${process.env.STRIPE_SECRET_KEY}`);

module.exports = {
  validateCoupon: async ctx => {
    const { coupon } = ctx.request.body;
    console.log('request coupon: ', coupon);

    try {
      const coupons = await stripe.coupons.list({ limit: 3 }, function (err, coupons) {
        console.log('err: ', err);
        console.log('coupons: ', coupons);
      });

      return coupons;
    } catch (err) {
      console.error('error validating coupon: ', err)
    }
  }
};

Right now, when I try to run the query in the GraphQL Playground, I get the error strapi.services.order.validateCoupon is not a function.

I'm fairly new to GraphQL... is there a better way to fetch external data than running a query?

****Update****

I've added my order service, which has gotten rid of that original error. The issue now is that even though the service appears to be returning the coupon correctly, the const entity in the schema.graphql.js returns undefined for some reason. I wonder if the resolver can't be async/await?

./order/services/order.js:

'use strict';
const stripe = require('stripe')(`${process.env.STRIPE_SECRET_KEY}`);

module.exports = {
  validateCoupon: ({ coupon }) => {
    stripe.coupons.list()
      .then(coupons => {
        return coupons.data.filter(c => {
          return (c.name === coupon && c.valid) ? c : null;
        });
        console.log('found: ', found);
      })
      .catch(err => console.error(err));
  }
};

Solution

  • So I ended up creating a Coupon model in the Strapi content builder. This enabled me to more easily return a Coupon object from my GraphQL query. It's not ideal because I'm having to make sure I create both a Stripe and Strapi coupon object to match, however I also don't anticipate on creating too many coupons in the first place.

    My updated code looks like this: schema.graphql.js:

    const { sanitizeEntity } = require('strapi-utils/lib');
    
    module.exports = {
      query: `
        validateCoupon(coupon: String): Coupon
      `,
      resolver: {
        Query: {
          validateCoupon: {
            description: 'Validate Stripe coupon',
            resolver: 'application::order.order.validateCoupon',
          }
        }
      }
    }
    

    ./order/controllers/order.js:

    'use strict';
    require('dotenv').config();
    
    const { sanitizeEntity } = require('strapi-utils');
    
    module.exports = {
      validateCoupon: async ctx => {
        const coupon = ctx.query._coupon;
        const found = await strapi.services.order.validateCoupon({ coupon });
        return sanitizeEntity(found, { model: strapi.models.order });
      }
    };
    

    ./order/services/order.js:

    'use strict';
    
    const stripe = require('stripe')(`${process.env.STRIPE_SECRET_KEY}`);
    
    module.exports = {
      async validateCoupon({ coupon }) {
        let foundCoupon = null;
    
        try {
          const coupons = await stripe.coupons.list();
          const found = coupons.data.filter(c => {
            return (c.name === coupon && c.valid) ? c : null;
          });
          if (found) foundCoupon = found[0];
        } catch (err) {
          console.error(err);
        }
    
        return foundCoupon;
      }
    };