StripeをAngularにStripeElementsと統合する
序章
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
にアクセスしようとすると文句を言います。 これを修正するために、プロジェクトのtypings.d.ts
ファイルに2つの宣言を追加します。
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>
#card-info
要素は、Stripe要素のコンテナーになります。また、エラーメッセージがある場合は、それを表示するためのコンテナー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
フックで削除するために使用されます。AfterViewInit
ライフサイクルフックのcard
要素を初期化して、コンテナー要素が使用可能であることを確認します。- ChangeDetectorRef を利用して、
onChange
メソッドで変更検出サイクルを実行するようにAngularに手動で指示します。 - フォーム送信メソッド
onSubmit
は、StripeのcreateToken
のawait
が解決することを約束する非同期関数です。 - 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); }
ストライプカード要素自体は、.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の顧客のために追加のデータも保存したい場合はどうでしょうか。 追加のフィールドを指定してcreateToken
に2番目の引数を渡すのと同じくらい簡単です。
たとえば、ここでは顧客の電子メールアドレスも送信します。
app.component.ts
async onSubmit(form: NgForm) { const { token, error } = await stripe.createToken(this.card, { email: this.emailAddress }); // ... }
結論
この投稿では、StripeAPIとStripeElementsを使用してAngularテンプレート駆動型フォームでフォームを作成しました。