nestjs-stripeモジュールで、NestJSからStripe APIを利用できるようにする

NestJSを利用すると、REST APIやGraphQL APIに静的なファイル配信など、さまざまな機能を備えたサーバーを作成できます。

https://nestjs.com/

今回の記事では、NestJSでStripeを簡単に利用できるライブラリ`nestjs-stripe`を紹介します。

https://github.com/dhaspden/nestjs-stripe

## NestJSプロジェクトのセットアップ

NestJSでアプリを作成するには、[Nest CLI]()を利用します。


`npm i -g @nestjs/cli`でインストールしましょう。

その後、`nest new <project-name>`でセットアップを開始できます。

```bash
$ nest new example-nestjs-stripe
? Which package manager would you ❤️  to use? npm
✔ Installation in progress... ☕

🚀  Successfully created project example-nestjs-stripe
👉  Get started with the following commands:

$ cd example-nestjs-stripe
$ npm run start

                                         
                          Thanks for installing Nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.
                                         
                                         
               🍷  Donate: https://opencollective.com/nest
```

### Nest CLIをグローバルインストールしない場合

NestJSはAngularやIonicと同じく専用のCLIコマンドを開発で多用します。

そのため、もしグローバルインストールしたくない場合は、`npm i -D @nestjs/cli`でプロジェクトの`node_modules`内にCLIを配置しましょう。

## Stripeモジュールを追加

`npm i nestjs-stripe stripe`でNestJSモジュールとStripe SDKを追加します。

```bash
$ npm i nestjs-stripe stripe
success Saved 2 new dependencies.
info Direct dependencies
├─ nestjs-stripe@1.0.0
└─ stripe@9.8.0
info All dependencies
├─ nestjs-stripe@1.0.0
└─ stripe@9.8.0
✨  Done in 2.99s.
```

### Stripe APIキーを環境変数経由で渡す

NestJSで環境変数を利用する場合、`@nestjs/config`モジュールを利用します。

```bash
$ npm i @nestjs/config
```

`.env.local`ファイルを作成し、StripeのシークレットAPIキーを設定しましょう。

```
STRIPE_SECRET_API_KEY=sk_test_xxxx
```

続いてStripe APIを呼び出したいモジュールにStripeモジュールを設定します。
`src/app.module.ts`に`ConfigModule`と`StripeModule`を追加しましょう。

```typescript
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigService } from '@nestjs/config';
import { StripeModule } from 'nestjs-stripe';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: ['.env.local'],
    }),
    StripeModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        apiKey: configService.get('STRIPE_SECRET_API_KEY'),
        apiVersion: '2020-08-27',
      }),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

```

## StripeのAPIを呼び出す

あとはStripeのAPIを呼び出したいServiceで`@InjectStripe`を実行するだけです。

`src/app.service.ts`を以下のように変更しましょう。

```typescript

import { Injectable } from '@nestjs/common';
import { InjectStripe } from 'nestjs-stripe';
import Stripe from 'stripe';

@Injectable()
export class AppService {
  constructor(
    @InjectStripe() private readonly stripeClient: Stripe
  ) {}
  
  public async listProducts() {
    return this.stripeClient.products.list();
  }

  getHello(): string {
    return 'Hello World!';
  }
}

```

続いて`listProducts`を呼び出すAPIを`src/app.controller.ts`に追加しましょう。

```typescript
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Get('products')
  async listProducts() {
    return this.appService.listProducts();
  }
}

```

これで、Stripeに登録した商品データを取得する`GET /products`APIが完成しました。

## Stripeモジュールをモックして、テストを書く

NestJSのモジュールを利用すると、Jestでのテストでモックがしやすくなるなどのメリットがあります。

### Stripeモジュールをモックする

Stripeモジュールをモックするには、`createTestingModule`を利用します。
まずはモック用のオブジェクトを用意しましょう。

```typescript
const stripeMock = () => ({
  products: {
    list: jest.fn().mockImplementation(() => [
      {
        id: 'prod_xxx',
      },
    ]),
  },
});
```

続いて、`createTestingModule`の`providers`でモジュールをモックします。
また、一緒に`module.get`でモックしたクラスも取得しておきましょう。

```typescript
import { Test, TestingModule } from '@nestjs/testing';
import { AppService } from './app.service';
import Stripe from 'stripe';

const stripeMock = () => {...}
 
describe('AppService', () => {
  let service: AppService;
  let stripe: Stripe;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AppService,
        {
          provide: 'StripeToken',
          useFactory: stripeMock,
        },
      ],
    }).compile();

    service = module.get<AppService>(AppService);
    stripe = module.get<Stripe>('StripeToken');
  });
...
```

### モックに対してテストを実行する

あとはテスト対象のメソッドを呼び出して、モックで設定した値を利用した結果をテストしましょう。
また、`module.get`しておけば、引数や呼び出し回数に関するテストも行えます。

```typescript
  it('listProducts', async () => {
    await expect(service.listProducts()).resolves.toEqual([
      {
        id: 'prod_xxx',
      },
    ]);
    expect(stripe.products.list).toHaveBeenCalledWith({
      expand: ['data.default_price'],
    });
  });
```

## 参考リンク

https://github.com/dhaspden/nestjs-stripe#getting-started

https://github.com/dhaspden/nestjs-stripe/issues/113

https://zenn.dev/waddy/articles/nestjs-configuration-service

https://docs.nestjs.com/fundamentals/testing

https://docs.nestjs.com/techniques/configuration#configuration


## [PR] Stripe開発者向け情報をQiitaにて配信中！

- [Stripe Updates]：開発者向けStripeアップデート紹介・解説
- ユースケース別のStripe製品や実装サンプルの紹介
- Stripeと外部サービス・OSSとの連携方法やTipsの紹介
- 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について週に２〜３本ペースで更新中です。

-> [Stripe Organizationsをフォローして最新情報をQiitaで受け取る](https://qiita.com/organizations/stripe)