# Stripe

This tutorial shows you how you can use Stripe package with Ts.ED.

The Stripe Node library provides convenient access to the Stripe API from applications written in server-side JavaScript.

For collecting customer and payment information in the browser, use Stripe.js (opens new window).

# Features

Currently, @tsed/stripe (opens new window) allows you to:

  • Configure Stripe,
  • Create webhooks,
  • Use Stripe API.

# Installation

To begin, install the Stripe module for Ts.ED:

npm install --save @tsed/stripe
npm install --save stripe
1
2

Then import @tsed/stripe in your Server:

import {Configuration, PlatformApplication} from "@tsed/common";
import "@tsed/stripe";
import {Stripe} from "stripe";

@Configuration({
  stripe: {
    apiKey: process.env.STRIPE_SECRET_KEY,
    webhooks: {
      secret: process.env.STRIPE_WEBHOOK_SECRET
    },
    // Stripe options
    apiVersion: "2019-08-08",
    httpProxy: new ProxyAgent(process.env.http_proxy)
  }
})
export class Server {
  @Inject()
  stripe: Stripe;

  $afterInit() {
    // do something with stripe
    // this.stripe.customers
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

TIP

See Stripe options for more details: https://www.npmjs.com/package/stripe

STRIPE_SECRET_KEY can be retrieved on the Stripe Dashboard here: https://dashboard.stripe.com/test/apikeys

And the STRIPE_WEBHOOK_SECRET can be retrieved by using the stripe listen or stripe forward:

stripe listen

> Ready! You are using Stripe API Version [2020-08-27]. Your webhook signing secret is whsec_*****************************
1
2
3

::: note You have to install the stripe-cli to run stripe listen. See https://stripe.com/docs/stripe-cli. :::

# Inject Stripe

import {Injectable} from "@tsed/di";

@Injectable()
class MyStripeService {
  @Inject()
  stripe: Stripe;

  $onInit() {
    // do something with stripe
    this.stripe.on("request", this.onRequest.bind(this));
  }

  protected onRequest(request: any) {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Webhook signing

Stripe can optionally sign the webhook events it sends to your endpoint, allowing you to validate that they were not sent by a third-party. You can read more about it here (opens new window).

To register a Stripe webhook with Ts.ED, just use the WebhookEvent decorator. It'll call for you the stripe.webhooks.constructEvent with the right parameters:

import {RawBodyParams, HeaderParams, Controller, Context} from "@tsed/common";
import {Stripe} from "stripe";

@Controller("/webhooks")
export class StripWebhookCtrl {
  @Inject()
  protected stripe: Stripe;

  @Post("/callback")
  successPaymentHook(@WebhookEvent() event: Stripe.Event, @Context() ctx: Context) {
    ctx.logger.info({name: "Webhook success", event});

    return {received: true};
  }

  // with custom webhook options
  @Post("/callback2")
  successPaymentHook(@WebhookEvent({secret: "....."}) event: Stripe.Event, @Context() ctx: Context) {
    ctx.logger.info({name: "Webhook success", event});

    return {received: true};
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Testing webhook

You can use stripe.webhooks.generateTestHeaderString to mock webhook events that come from Stripe:

import {Stripe} from "stripe";
import {PlatformTest} from "@tsed/common";
import {StripWebhookCtrl} from "./StripWebhookCtrl";

describe("StripWebhookCtrl", () => {
  beforeEach(() =>
    PlatformTest.create({
      stripe: {
        apiKey: "fake_api_key",
        webhooks: {
          secret: "whsec_test_secret"
        },
        // Stripe options
        apiVersion: "2019-08-08"
      }
    })
  );
  afterEach(PlatformTest.reset);
  it("should call event", async () => {
    const stripe = PlatformTest.get<Stripe>(Stripe);
    const payload = {
      id: "evt_test_webhook",
      object: "event"
    };
    const payloadString = JSON.stringify(payload, null, 2);

    const header = stripe.webhooks.generateTestHeaderString({
      payload: payloadString,
      secret: "whsec_test_secret"
    });

    const event = stripe.webhooks.constructEvent(payloadString, header, secret);
    const ctx = PlatformTest.createRequestContext();

    const ctrl = await PlatformTest.invoke<StripWebhookCtrl>(StripWebhookCtrl);

    const result = ctrl.successPaymentHook(event, ctx);

    expect(result).toEqual({received: true});
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

With SuperTest:

import {PlatformTest} from "@tsed/common";
import {PlatformExpress} from "@tsed/platform-express";
import {PlatformTestUtils} from "@tsed/platform-test-utils";
import {expect} from "chai";
import {Stripe} from "stripe";
import SuperTest from "supertest";
import {StripeWebhooksCtrl} from "./StripWebhookCtrl";
import {rootDir, Server} from "../Server";

const utils = PlatformTestUtils.create({
  rootDir,
  platform: PlatformExpress,
  server: Server,
  logger: {
    level: "info"
  }
});

describe("Stripe", () => {
  let request: SuperTest.Agent;
  beforeEach(
    utils.bootstrap({
      mount: {
        "/rest": [StripWebhookCtrl]
      }
    })
  );
  beforeEach(() => {
    request = SuperTest.agent(PlatformTest.callback());
  });

  afterEach(() => PlatformTest.reset());

  it("should call the webhook", async () => {
    const stripe = PlatformTest.get<Stripe>(Stripe);
    const payload = {
      id: "evt_test_webhook",
      object: "event"
    };
    const payloadString = JSON.stringify(payload, null, 2);

    const signature = stripe.webhooks.generateTestHeaderString({
      payload: payloadString,
      secret: "whsec_test_secret"
    });

    const response = await request.post("/rest/webhooks/callback").send(payloadString).set("stripe-signature", signature).expect(200);

    expect(response.body).to.deep.eq({
      event: payload,
      received: true
    });
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# Known issue

If you have the followings message, it means you have an issue with your STRIPE_WEBHOOK_SECRET.

Error message: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
1

Please double-check your configuration!

# Author

    # Maintainers

      Last Updated: 10/24/2024, 6:33:40 AM

      Other topics