会社仕事でも個人ビジネスでも、商品やサービスの対価として利用者に課金方法を提供したい時があるかと思います。
最もメジャーな決済方法であるクレジットカードで課金してもらう仕組みを導入したいところですが、開発システム側でクレジットカード番号を保持したり、通過させたりしてしまうと「PCI-DSS」という高難度のセキュリティ基準に従う必要が出てきます。
PCI-DSSに準拠するには堅牢なシステム構築の他、サーバ配置施設への入退館を記録、サーバルームへの入退室を記録、監視カメラの設置、他様々な要件をクリアしないと認可が下りません。
人様のカード番号を預かる訳ですから当然と言えば当然ですが、コストが掛かり過ぎて中小や個人レベルでは実現が難しいですよね。
こんな時に助け船になってくれるのが自分達の開発システムの替わりにクレジット決済をしてくれる「決済代行サービス」です。
PayPal、SPIKE、Pay.jpなど数ある決済代行サービスの中、今回はデファクトスタンダードになりつつある「Stripe」で決済までの流れを実装していきたいと思います。
目次
Stripeとは
- 単発課金やサブスクリプションが可能。
- 決済手数料3.6%。
- アプリに組み込みやすい(決済画面に遷移しなくてよい、Payment Request APIと連携可)
- 様々な言語のライブラリが用意されている。
- 公式ドキュメントが豊富。
手っ取り早くスマホ、PCブラウザで動作を見たい場合は以下のデモが便利です。
上記デモ開き、ブラウザのデベロッパーツールでHTMLエレメントを確認すると、カード番号入力欄はstripeサーバから提供されるHTMLソースをiframeで読み込んでいて、こちらが開発するHTMLソースとは分離されていることが確認できます。フロントエンド側でも非保持が出来ていそうです。
検証構成
色んな構成形態をとることが出来るStripeですが今回はこんな感じで単純な実装をしてみます。
カード番号に対する「トークン」を発行してもらうことでPCI-DSS準拠を回避します。開発するシステムではユーザの選択したカード番号を知ることは出来ません。
商品が渡せなかった場合は決済をしないような処理も出来るので、GooglePlayやAppStoreのアプリ内課金より柔軟でいいですね。
検証環境
- Windows 10
- Eclipse 2019-03
- Java8
localhostで動作確認する分にはHTTP環境でOKです。スマホなど外部ホストから接続する時はHTTPS環境が必要になります。
HTTPS環境がない場合はngrokで代替すればOKです。
シンプルに実装して決済まで確認してみる
Stripeのクレジットカード入力画面をJavascriptで表示する場合、主に3つの方法があります。
- クレジット入力画面をカスタマイズ出来るStripe Element
- 出来合いのダイアログを表示出来るcheckout.js
- W3C標準のPayment Request APIと連携するPayment Request Button
今回は決済完了までの動作確認をするだけなので、最も簡単な2で決済までやってみます。
実戦に導入するなら、以前ブラウザに入力したことがあるカードが候補に出る3にするか、ダイアログではなくページに溶け込ませることが出来る1も検討に入れます。
1.stripe.comでアカウントを作る
stripe.comで「今すぐ始める」ボタンをクリックするとアカウントが作れます。テスト環境を使うだけなら数分で終わります。このあたりの簡単さもStripeのシェア拡大に一役買ってそうですね。
ログインしてダッシュボードに入り、「テストAPIキー」の公開可能キーとシークレットキーを確認しておきます。後でプログラムから指定する為です。
2.SpringBootプロジェクトを作成
フロントエンドのHTML、バックエンドHTTPサーバを作る為のプロジェクトをSpringBootで適当に作っておきます。(作成したことがなければ以下参照)
3.フロントエンドHTMLを作成
公式ドキュメントを参考にしながら実装していきます。
/src/main/resource/static/stripe-checkout.htmlを作成して以下を記述します。data-keyには先程作ったアカウントの「公開可能キー」を指定します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Stripe checkout.jsの動作確認</title>
</head>
<body>
<form action="/pay" method="POST">
<script
src="https://checkout.stripe.com/checkout.js"
class="stripe-button"
data-key="pk_test_xxxxxxxxxxxxxxxxxxxxxxxxx"
data-name="Tシャツ"
data-description="コットンTシャツ"
data-amount="500"
data-currency="jpy">
</script>
</form>
</body>
</html>
http://localhost:8080/stripe-checkout.htmlにアクセスするとカード番号入力ダイアログが表示されます。「TEST MODE」ボタンを押してテスト用ダミーカード番号を確認、指定してみましょう。
ただこの状態でPayボタンをクリックしても、formアクションのPOST先「/pay」が無いのでエラーになってしまいます。Javaでサーバ側を実装していきましょう。
4.バックエンドJavaを実装
mvnrepositoryで「stripe-java」の最新バージョンを探してpom.xmlに依存を追加します。
このjarを使うとStripeサーバに対して決済依頼やトークンのチェックをリクエストすることが出来るようになります。
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>14.3.0</version>
</dependency>
Spring MVCで/payを実装します。Stripe.apiKeyにはご自分のアカウントの「シークレットキー」を指定してください。
package com.example.demo;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.Charge;
@Controller
public class DemoServer {
@RequestMapping(path = "/pay", method = { RequestMethod.POST })
public ResponseEntity charge(
@RequestParam("stripeToken") String stripeToken,
@RequestParam("stripeTokenType") String stripeTokenType,
@RequestParam("stripeEmail") String stripeEmail)
{
Stripe.apiKey = "sk_test_xxxxxxxxxxxxxxxxxxxxxxxx";
Map<String, Object> chargeMap = new HashMap<String, Object>();
chargeMap.put("amount", 500);
chargeMap.put("description", "コットンTシャツ");
chargeMap.put("currency", "jpy");
chargeMap.put("source", stripeToken);
try {
Charge charge = Charge.create(chargeMap);
System.out.println(charge);
} catch (StripeException e) {
e.printStackTrace();
}
ResponseEntity response = ResponseEntity.ok().build();
return response;
}
実装完了です。
動作確認
再度 http://localhost:8080/stripe-checkout.html にアクセスしてPayボタンをクリックしてみると今度は正常に終了し、EclipseのコンソールにはStripeサーバから決済が完了した旨のJSONが返却されているはずです。
<com.stripe.model.Charge@1751915628 id=ch_1FYCH3HZxxxxxxxxxxyY1NS> JSON: {
"alternate_statement_descriptors": null,
"amount": 500,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"application_fee_amount": null,
(snip)
ブラウザでStripeのダッシュボードを開いて決済が完了しているか見てみましょう。左メニュー「残高」→「取引」で確認できます。
決済されていました。手数料3.6%なので500円の内18円がStripeに渡っていますね。
(テスト環境なので実際に入金はされていません)
Stripeダッシュボードで決済が完了していることが確認できました。
まとめと課題
テスト環境とはいえ少ない労力でクレジットカード課金を実装することが出来ました。
本番に移行する際はサイトURLの登録や審査を通し、発行された本番APIキーに切り替えるだけです。ほんとにシンプルで便利ですね。
ただ今回の動作確認は最も簡単な実装なので色々課題や要望が残ります。
- FORM送信だとページ遷移してしまったりトークン取得とサーバへの決済依頼が同時に発生して使いづらいのでAjax送信にしたい。
- サーバ側もそれに合わせてJSON受信するRESTにしたい。
- 課金ダイアログはW3C標準APIのPayment Request APIにしたい。
- Google Pay、Apple Payを使ってクレジットカード選択を楽にしたい。
次回はこれらをやってみたいと思います。