Stripeのオンライン決済をTypescriptで実装する(Charges)

StripeはTypescriptで書こう
2020.06.16

クレジットカードでの都度決済をStripeで実装するときはPAYMENTという機能を使います。
今回はStripeのオンライン決済を実装する際のサーバー側の一通りの操作をTypescriptで実装してみます。

Stripe Paymentsについて

StripeではCheckoutというStripeが提供する決済フォームにリダイレクトする機能があります。
ここでは自前のクレジットカード決済フォームで決済するケースを想定して、Cherge APIを使う前提で進めていきます。
またクライアント側の実装については今回は触れません。

2020/08/19 追記
こちらのブログに書いたように、現在ChergesよりPaymentIntentsを使って実装することが推奨されています。
新しく実装される際にはPaymentIntentsを検討してください。

動作確認環境

インストール

npm install stripe --save
npm install @types/stripe –save-dev

importとキーの読み込み

import Stripe from 'stripe';
const stripe = new Stripe('sk_test_xxxxxx');

バージョニング

StripeではSDKのバージョンとは別に実行するStripe APIのバージョンを切り替えることができます。

ちなみに、以下のように指定することでAPIのバージョンを切り替えることができます。

const stripe = new Stripe('sk_test_xxxxxx', { apiVersion: '2020-03-02' });

未指定の場合はStripeアカウントに設定されているバージョンがデフォルトとして適用されます。
デフォルトのバージョンを切り替えたい場合はダッシュボードの 開発 -> APIバージョン のところから切り替えられます。

各バージョンのアップデート内容はこちらから確認して下さい。

ここでは現時点(2020/06/16)の最新版である 2020-03-02 を使っています。

各操作

カスタマーの登録 (customers.create)

const createCustomer = async (options: Stripe.customers.ICustomerCreationOptions) => {
    return await stripe.customers.create(options);
}

Token or Sources APIを使用して取得した source で、顧客作成とともにカード登録を行います。 サーバー側の動作のみ確認したい場合は、Stripeで用意されているテストトークンを利用しましょう。
tok_jpでVISA・tok_jcbでJCBのカードと見立ててテストができます。詳しくはTesting ドキュメントで確認してください。

カスタマー登録時には他にもオプションがあってemailを登録すると、Stripeから領収書メールを自動送信したりできます。

カードの登録 (customers.createSource)

const createSource = async (customerId: string, source: string) => {
    return await stripe.customers.createSource(customerId, { source });
}

sourceにはクライアント向けのJSライブラリで取得したTokenを渡します。

Tokenをサーバー側に渡すことで、サーバー側ではユーザーのカード情報を保持せずにStripeに登録することができます。

カードの削除 (customers.deleteSource)

const deleteSource = async (customerId: string, cardId: string) => {
    return await stripe.customers.deleteSource(customerId, cardId);
}

カード一覧取得 (customers.listSources)

const listSources = async (customerId: string, options: Stripe.customers.ICardSourceListOptions) => {
    return await stripe.customers.listSources(customerId, options);
}

const result = await listSources('cus_xxxxxx', { object: 'card'});

請求 (charges.create)

charges.createで請求を作成します。

新しいカードで決済する

interface chargeOptions {
    amount: number;
    currency: 'jpy';
    source: string;
};

const charge = async (options: chargeOptions) => {
    return await stripe.charges.create(options);
}

登録したカードで決済する

interface chargeOptions {
    amount: number;
    currency: 'jpy';
    customer: string;
};

const charge = async (options: chargeOptions) => {
    return await stripe.charges.create(options);
};

オーソリを追加する

請求を作成する際に決済にオーソリを追加することができます。
決済確定までに何らかの処理を挟みたい場合や、返金までの猶予期間を設けたい場合に使いましょう。
charges.create のoptionsのcaptureをfalseにすることで、決済を確定させずに決済承認のみを発行することができます。

const charge = async (options: Stripe.charges.IChargeCreationOptions) => {
    return await stripe.charges.create(options);
};

const options = {
    amount: 100,
    currency: 'jpy',
    customer: 'cus_xxxxxx',
    capture: false
};

const result = await charge(options);

決済の確定 (charges.capture)

charges.createの時にcaptureを分離した場合は、charges.captureで決済を確定します。

const capture = async (chargeId: string, options: Stripe.charges.IChargeCaptureOptions) => {
    return await stripe.charges.capture(chargeId, options);
};

返金 (refunds.create)

返金はrefunds.createで実行します。
オーソリでもcapture済でもrefundで返金できますが、capture済の場合加盟店手数料がかかります。

const refund = async (options: Stripe.refunds.IRefundCreationOptionsWithCharge) => {
    return await stripe.refunds.create(chargeId, options);
};

const options = {
    charge: 'ch_xxxxxx'
};

const result = await refund(options);

余談

私の環境での話ですが、@types/stripe: 7.13.23@types/node 13.11.0 と併用した時に、以下のコンフリクトが発生してしまいました。

error TS4090: Conflicting definitions for 'node' found at '/path/to/project/src/node_modules/@types/node/ts3.7/index.d.ts' and '/path/to/project/src/node_modules/@types/node/ts3.2/index.d.ts'. Consider installing a specific version of this library to resolve the conflict.

@types/nodeを14.0.1に更新したらエラーが解消しました。

参考

paymentsドキュメント