npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

feathers-stripe

v3.0.0

Published

A Feathers service for Stripe

Downloads

490

Readme

feathers-stripe

CI Dependency Status Download Status Discord

A Feathers service for Stripe

Installation

npm install stripe@^13 feathers-stripe --save

Documentation

Please refer to the Stripe API Docs and the stripe-node docs for options that can be passed. Feathers service methods map to the following Stripe methods:

  • Feathers find -> Stripe list
  • Feathers get -> Stripe retrieve
  • Feathers create -> Stripe create
  • Feathers update -> Stripe update
  • Feathers patch -> Stripe update (in most cases). Some special cases include paying an invoice or an order when you pass {pay: true} as part of context.data. See each service's code for more info.
  • Feathers remove -> Stripe del (in most cases). Some special cases include transfers and charges create a reversal/refund. See each service's code for more info.

If a method is not supported by Stripe for a given resource it is not supported here as well.

Use params.stripe to pass additional parameters like expand, idempotencyKey, apiVersion, etc to the underlying Stripe methods.

Many methods support/require passing special properties to context.data and context.query to better inform the underlying stripe methods. You are encouraged to read the source code for each service to better understand their usage. For example, the Card service requires a customer to be provided.

const card = await app.service("stripe/cards").create({
  customer: "cust_123",
  source: { token: "tok_123" }
});
// stripe.customers.createSource(customer, { source: { ... } });

const card = await app.service("stripe/cards").get("card_123", {
  query: { customer: "cust_123" }
});
// stripe.customers.retrieveSource(query.customer, id);

Available Services

The following services are supported and map to the appropriate Stripe resource:

  • AccountLinks
  • Account
  • ApplicationFeeRefund
  • Balance
  • BankAccount
  • Card
  • Charge
  • Coupon
  • Customer
  • Dispute
  • Event
  • ExternalAccount
  • InvoiceItem
  • Invoice
  • PaymentIntent
  • PaymentMethod
  • Payout
  • Plan
  • Price
  • Product
  • Recipient
  • Refund
  • SetupIntent
  • Source
  • SubscriptionItem
  • Subscription
  • Token
  • Transaction
  • TransferReversal
  • Transfer
  • Webhook

This is pretty important! Since this connects to your Stripe account you want to make sure that you don't expose these endpoints via your app unless the user has the appropriate permissions. You can prevent any external access by doing this:

import { Forbidden } from "@feathersjs/errors";

app.service("stripe/cards").hooks({
  before: {
    all: [
      (context) => {
        if (context.params.provider) {
          throw new Forbidden("You are not allowed to access this");
        }
      }
    ]
  }
});

This is pretty important! You are also encouraged to use some kind of rate limiter. Checkout the Stripe Rate Limit Docs

import Bottleneck from 'bottleneck';

// Configure 100 reqs/second for production, 25 for test mode
const readLimiter = new Bottleneck({ minTime: 10 });
const writeLimiter = new Bottleneck({ minTime: 10 });
// const readLimiter = new Bottleneck({ minTime: 40 });
// const writeLimiter = new Bottleneck({ minTime: 40 });

const rateLimitHook = async (context) => {
  const limiter = context.method === 'find' || context.method === 'get'
    ? readLimiter
    : writeLimiter;

  context.result = await limiter.schedule(() => {
    // Use an underscored method to bypass hooks and not end
    // up in an infinite loop hitting this hook again.
    if (context.method === 'find') {
      return context.service._find(context.params);
    }
    if (context.method === 'get') {
      return context.service._get(context.id, context.params);
    }
    if (context.method === 'create') {
      return context.service._create(context.data, context.params);
    }
    if (context.method === 'update') {
      return context.service._update(context.id, context.data, context.params);
    }
    if (context.method === 'patch') {
      return context.service._patch(context.id, context.data, context.params);
    }
    if (context.method === 'remove') {
      return context.service._remove(context.id context.params);
    }
  });

  return context;
}

// The rateLimitHook should be the last before hook for each method.
app.service('stripe/cards').hooks({
  before: {
    find: [ ...hooks, rateLimitHook],
    get: [ ...hooks, rateLimitHook],
    create: [ ...hooks, rateLimitHook],
    update: [ ...hooks, rateLimitHook],
    patch: [ ...hooks, rateLimitHook],
    remove: [ ...hooks, rateLimitHook],
  }
});

Complete Example

Here's an example of a Feathers server that uses feathers-authentication for local auth. It includes a users service that uses feathers-mongoose. Note that it does NOT implement any authorization.

import { feathers } from "@feathersjs/feathers";
import express from "@feathersjs/express";
import socketio from "@feathersjs/socketio";
import Stripe from 'stripe';
import { ChargeService } from "feathers-stripe";

// Initialize the application
const app = feathers()
  .configure(express.rest())
  .configure(socketio())
  // Needed for parsing bodies (login)
  .use(express.json())
  .use(express.urlencoded({ extended: true }))
  // A simple Message service that we can used for testing
  .use(
    "/stripe/charges",
    new ChargeService({ stripe: new Stripe('sk_test_...') })
  )
  .use("/", feathers.static(__dirname + "/public"))
  .use(express.errorHandler({ html: false }));

const validateCharge = () => (hook) => {
  console.log("Validating charge code goes here");
};

const chargeService = app.service("stripe/charges");

chargeService.hooks({
  before: {
    create: [validateCharge()]
  }
});

const charge = {
  amount: 400,
  currency: "cad",
  source: "tok_87rau6axWXeqLq", // obtained with Stripe.js
  description: "Charge for [email protected]"
};

chargeService
  .create(charge)
  .then((result) => {
    console.log("Charge created", result);
  })
  .catch((error) => {
    console.log("Error creating charge", error);
  });

app.listen(3030);

console.log("Feathers authentication app started on 127.0.0.1:3030");

Webhook

You can setup a webhook using the helper function setupWebhook in your service

export default function (app) {
  setupWebhook(app, "/stripe-webhook", {
    endpointSecret: "whsec_ededqwdwqdqwdqwd778qwdwqdq",
    stripe: new Stripe("sk_test_..."),
    handlers: {
      customer: {
        subscription: {
          async created({ object, event, params, app }) {
            return {};
          }
        }
      }
    }
  });

  // Get our initialized service so that we can register hooks
  const service = app.service("stripe-webhook");

  service.hooks(hooks);
}

License

Licensed under the MIT license.