序章

Stripeは最近StripeElements をリリースしました。これは、リアルタイムの検証とオートコンプリートのサポートを備えた、カスタムチェックアウトフローの構築を容易にするUI要素のセットです。 この投稿では、フロントエンドのStripeおよびStripeElementsをAngularと統合するための基本について説明します。

StripeAPIからトークンを取得する簡単な支払いフォームを作成します。 Elementsに関するStripeのドキュメントは、バニラJavaScriptを使用して統合するためのものですが、ここでは、Angularテンプレート駆動型フォームと統合するために、この実装を少し変更します。

プロジェクトの設定

まず、プロジェクトの index.html ファイル、追加して初期化する必要があります Stripe.js ストライプ要素を初期化するだけでなく:

index.html
...
<body>
  <app-root></app-root>

  <script src="https://js.stripe.com/v3/"></script>
  <script type="text/javascript">
    var stripe = Stripe('pk_test_XXXXXXXXXXXXXXXXX'); // use your test publishable key
    var elements = stripe.elements();
  </script>
</body>
</html>

警告:アプリが本番環境に移行したら、ライブの公開可能なキーに変更することを忘れないでください。

以来 Stripe.js プロジェクトの範囲外に追加され、型指定がない場合、TypeScriptは通常、アクセスしようとすると文句を言います。 stripe また elements. これを修正するために、プロジェクトに2つの宣言を追加します typings.d.ts ファイル:

types.d.ts
// ...

declare var stripe: any;
declare var elements: any;

単純な支払いフォームにはAngularのテンプレート駆動型フォームを使用するため、アプリまたは機能モジュールにFormsModuleもインポートする必要があります。

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import {FormsModule} from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

フォームの作成

基本的なチェックアウトフローのテンプレートマークアップは、次のように簡単です。

app.component.html
<form #checkout="ngForm" (ngSubmit)="onSubmit(checkout)" class="checkout">
  <div class="form-row">
    <label for="card-info">Card Info</label>
    <div id="card-info" #cardInfo></div>

    <div id="card-errors" role="alert" *ngIf="error">{{ error }}</div>
  </div>

  <button type="submit">Pay $777</button>
</form>

The #card-info elementはStripeElementsのコンテナになります。また、エラーメッセージがある場合はそれを表示するためのコンテナdivも作成しました。

楽しい部分は、コンポーネントクラスにすべてを接続することから始まります。 いくつかの興味深い部分を強調して、例を機能させるためのコードを次に示します。

app.components.ts
import {
  Component,
  AfterViewInit,
  OnDestroy,
  ViewChild,
  ElementRef,
  ChangeDetectorRef
} from '@angular/core';

import { NgForm } from '@angular/forms';

@Component({ ... })
export class AppComponent implements AfterViewInit, OnDestroy {
  @ViewChild('cardInfo') cardInfo: ElementRef;

  card: any;
  cardHandler = this.onChange.bind(this);
  error: string;

  constructor(private cd: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.card = elements.create('card');
    this.card.mount(this.cardInfo.nativeElement);

    this.card.addEventListener('change', this.cardHandler);
  }

  ngOnDestroy() {
    this.card.removeEventListener('change', this.cardHandler);
    this.card.destroy();
  }

  onChange({ error }) {
    if (error) {
      this.error = error.message;
    } else {
      this.error = null;
    }
    this.cd.detectChanges();
  }

  async onSubmit(form: NgForm) {
    const { token, error } = await stripe.createToken(this.card);

    if (error) {
      console.log('Something is wrong:', error);
    } else {
      console.log('Success!', token);
      // ...send the token to the your backend to process the charge
    }
  }
}

一見、多くのことをしているように見えるかもしれませんが、それはすべて本当に簡単です。 注意すべき点がいくつかあります。

  • カード要素のコンテナにアクセスするには、 ViewChild デコレータ。
  • 私たちは onChange 方法 this クラスの名前を付けて、新しい参照を次のように保存します cardHandler. この参照は、カード要素が作成されたときにイベントリスナーを追加し、カード要素でそれを削除するために使用されます。 OnDestroy 針。
  • 初期化する card の要素 AfterViewInit ライフサイクルフック、コンテナ要素が使用可能であることを確認します。
  • ChangeDetectorRef を利用して、Angularに変更検出サイクルを実行するように手動で指示します。 onChange 方法。
  • 私たちのフォーム送信方法、 onSubmit、は非同期関数であり、 awaitsストライプの createToken 解決することを約束します。
  • Stripeから有効なトークンを取得したら、トークンをバックエンドまたはクラウド機能に送信して請求を処理する必要があります。
  • の中に OnDestroy 変更イベントリスナーを削除し、カード要素を破棄してクリーンアップします。

この例は実際には必要最低限のものですが、実際のアプリでは、ユーザーがフォームを連続して複数回送信できないようにする単純なブールフラグも実装する必要があります。 たとえば、フォームが送信されてからバックエンドが請求が処理されたことを示す成功メッセージを送信するまで、送信ボタンを読み込みインジケーターに置き換えることができます。 その場合、ユーザーを確認ページのようなものにリダイレクトします。

注:テストするには、カード番号 4242 4242 4242 4242 を使用し、将来の有効期限、CVCの3桁の番号、および有効な郵便番号を指定します。

スタイルのカスタマイズ

簡単なチェックアウトフォームが機能していますが、かなり退屈に見えます。 ちょっとしたスタイルを加えましょう。 まず、フォームのスタイルを設定してボタンを送信しましょう。

app.component.css
form.checkout {
  max-width: 500px;
  margin: 2rem auto;
  text-align: center;
  border: 2px solid #eee;
  border-radius: 8px;
  padding: 1rem 2rem;
  background: white;

  font-family: monospace;
  color: #525252;
  font-size: 1.1rem;
}

form.checkout button {
  padding: 0.5rem 1rem;
  color: white;
  background: coral;
  border: none;
  border-radius: 4px;
  margin-top: 1rem;
}

form.checkout button:active {
  background: rgb(165, 76, 43);
}

Stripeカード要素自体は、次のようなセレクターを使用してスタイルを設定できます。 .StripeElement, .StripeElement--focus.StripeElement--invalid. 利用可能な既製のテーマの例がいくつかありますが、ここではStripeが提供するデフォルトのスタイルを追加します。

...

.StripeElement {
  margin: 1rem 0 1rem;
  background-color: white;
  padding: 8px 12px;
  border-radius: 4px;
  border: 1px solid transparent;
  box-shadow: 0 1px 3px 0 #e6ebf1;
  -webkit-transition: box-shadow 150ms ease;
  transition: box-shadow 150ms ease;
}

.StripeElement--focus {
  box-shadow: 0 1px 3px 0 #cfd7df;
}

.StripeElement--invalid {
  border-color: #fa755a;
}

.StripeElement--webkit-autofill {
  background-color: #fefde5 !important;
}

一部の基本的なスタイルは、createメソッドの2番目の引数としてオプションオブジェクトを使用して渡すこともできます。

app.component.ts(部分的)
ngAfterViewInit() {
  const style = {
    base: {
      lineHeight: '24px',
      fontFamily: 'monospace',
      fontSmoothing: 'antialiased',
      fontSize: '19px',
      '::placeholder': {
        color: 'purple'
      }
    }
  };

  this.card = elements.create('card', { style });
  this.card.mount(this.cardInfo.nativeElement);

  this.card.addEventListener('change', this.cardHandler);
}

可能なすべてのスタイル構成オプションのリストについては、オプションAPIリファレンスを参照してください。

追加フィールドの保存

私たちのチェックアウトフォームはすべてうまくいっていますが、Stripeの顧客のために追加のデータも保存したい場合はどうでしょうか。 2番目の引数をに渡すのと同じくらい簡単です createToken 余分なフィールドがあります。

たとえば、ここでは顧客の電子メールアドレスも送信します。

app.component.ts
async onSubmit(form: NgForm) {
  const { token, error } = await stripe.createToken(this.card, {
    email: this.emailAddress
  });

  // ...
}

結論

この投稿では、StripeAPIとStripeElementsを使用してAngularテンプレート駆動型フォームでフォームを作成しました。