android iOS java javascript 決済

Stripe+Java+Payment Request APIでApple Pay、Google Payを使ったテストWeb決済をしてみる

投稿日:2020年2月9日

自分で作ったサービスを運用してチャリンチャリンしたい・・・
エンジニアならこんな夢、一度は見たことがあるんじゃないでしょうか。

夢を実現する為、以前Stripeのcheckout.jsを使ったテストWeb決済をしてみました。

この時こんな課題を挙げました。

  • FORM送信だとページ遷移してしまったりトークン取得とサーバへの決済依頼が同時に発生して使いづらいのでJSON送信するAjax送信にしたい。
  • サーバ側もそれに合わせてJSON受信するRESTにしたい。
  • 課金画面はW3C標準APIのPayment Request APIにしたい。
  • Google Pay、Apple Payを使ってクレジットカード選択を楽にしたい。

今回はこれらの課題を解決出来るか検証して行きたいと思います。

前提準備

サンドボックステスターを作っておく

Apple Payでテスト課金する為にサンドボックステスターを作り、テスターのApple IDでiPhoneにログインしておきます。

Appleにドメイン登録をしておく(Stripeを使えば例外も有り)

Apple Payは基本的に(次項参照)登録したドメインでしか課金確認画面を出してくれません。事前にApple Develper Programで設定をしておきます。

要HTTPS環境

W3C標準の課金APIであるPayment Request APIはlocalhostならHTTPでも動作してくれます。しかしApple Payは登録したドメインでかつHTTPSでないと動きません。

この為、インターネット外部からアクセスが可能なHTTPSサーバを開発環境にする必要があり、私の場合は自宅に以下のような環境を構築して検証しました。

検証するだけでここまでする必要あるのか・・・って話ですが、有難いことにstripe側で救済策を用意してくれていて、Appleにドメイン登録しなくても開発は出来るようになっています。 (以下サイトの「Verify your domain with Apple Pay」の項参照)

Appleに登録した単一のドメインでしか動作確認が出来ない、といった状況は免れられるので、制約の厳しい職場などでは試してみる価値があると思います。

実装開始

冒頭のcheckout.jsを使ったStripe決済記事で作成したSpringBootプロジェクトを使って実装していきます。

フロントエンドHTML、JSの実装

/src/main/resource/static/stripe-pra.htmlを作成して以下を記述。Stripeサーバから課金トークンを貰い、fetchでサーバに課金トークンを含んだJSONを送信。

W3C標準課金APIのPayment Request APIをStripe経由で使うには、

  • https://js.stripe.com/v3/をロード。
  • new PaymentRequest()の替わりにstripe.paymentRequest()を使用。

概ねこれだけで各ブラウザでのPayment Request画面表示、カード番号をstripeへ送信、課金トークンの受領までやってくれます。便利。

<html>
<head>
<script src="https://js.stripe.com/v3/"></script>
</head>

<body>
    <div id="payment-request-button">
        <!-- Payment Request APIのボタンが配置される場所 -->
    </div>

    <script>
        // Apple Payが使えるようになっているか一応チェック
        if (window.ApplePaySession) {
            console.log("window.ApplePaySession", window.ApplePaySession);
            console.log("ApplePaySession.canMakePayments()", ApplePaySession.canMakePayments());
            console.log("window.PaymentRequest", window.PaymentRequest);
            console.log("ApplePaySession.canMakePaymentsWithActiveCard()",
                    ApplePaySession.canMakePaymentsWithActiveCard('pirhana.dix.asia'));
        }

        // 自分のStripeのパブリックキーを使用
        var stripe = Stripe('pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');

        // Stripe API経由でPaymentRequestオブジェクト生成、ボタンを配置
        var paymentRequest = stripe.paymentRequest({
            country : 'JP',
            currency : 'jpy',
            total : {
                label : 'Tシャツ',
                amount : 500,
            },
            requestPayerName : false,
            requestPayerEmail : false,
            requestPayerPhone: false,
            requestShipping : false
        });
        var elements = stripe.elements();
        var prButton = elements.create('paymentRequestButton', {
            paymentRequest : paymentRequest,
        });

        // Stripe経由でPayment Request APIが使えるかチェック
        paymentRequest
            .canMakePayment()
            .then(function(result) {
                    // Payment Request API使用可能
                    if (result) {
                        // Payボタンを配置
                        prButton.mount('#payment-request-button');
                    } else {
                        document.getElementById('payment-request-button').style.display = 'none';
                    }
                });

        // 支払いボタンクリック後、stripeサーバからトークンを受信
        paymentRequest.on('token', function(ev) {
            let token = ev.token;

            // バックエンドJavaに送信するJSON
            let chargeRequest = {
                "stripeToken" : token.id,
                "stripeTokenType" : token.type,
                "stripeEmail" : token.email
            };

            // バックエンドJavaのRESTに課金トークンを送信
            // 「stripetest」パスで宅内PCのSpringBootにリバプロされるようにApacheを設定
            fetch('/stripetest/charge', {
                method : 'POST',
                body : JSON.stringify(chargeRequest),
                headers : {
                    'content-type' : 'application/json'
                },
            }).then(function(response) {
                if (response.ok) {
                    ev.complete('success');
                } else {
                    ev.complete('fail');
                }
            });
        });
    </script>
</body>
</html>

バックエンドJavaの実装

クライアントJSからはJSONでトークンを送信するようにしたので、JSONを受信出来るようにSpringMVCのアノテーションを変更しておきます。

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.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.Charge;

@Controller
public class DemoServer {

    @RequestMapping(path = "/charge", produces = "application/json", method = { RequestMethod.POST })
    public ResponseEntity charge(@RequestBody ChargeRequest request) {

        System.out.println("stripeToken = " + request.stripeToken);

        // 自分のStripeシークレットキーを使用
        Stripe.apiKey = "sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

        // Stripeサーバに決済依頼する内容
        Map<String, Object> chargeMap = new HashMap<String, Object>();
        chargeMap.put("amount", 500);
        chargeMap.put("description", "Tシャツ");
        chargeMap.put("currency", "jpy");
        chargeMap.put("source", request.stripeToken);

        try {
            // 決済依頼
            Charge charge = Charge.create(chargeMap);
            System.out.println(charge);
        } catch (StripeException e) {
            e.printStackTrace();
        }

        ResponseEntity response = ResponseEntity.ok().build();
        return response;
    }
}

JSON受信する為のマッパーPOJO。

package com.example.demo;

public class ChargeRequest {
    public String stripeToken;
    public String stripeTokenType;
    public String stripeEmail;
}

Eclipse上でプロジェクトを右クリック → 実行 → SpringBootアプリケーションでHTTPサーバを起動して動作確認準備完了です。

動作確認

https://[Appleに登録したドメイン]/stripetest/stripe-pra.html

にiPhone(Safari)とAndroid(Chrome)でアクセスしてみます。

Apple Pay

サンドボックステスターでログイン、Walletにテストカードを登録したiPhone実機です。

PayボタンをクリックしてTouchIDで認証すると、正常に処理が終了しました。

Google Pay

Google Payはテスト課金なら、Googleにドメイン登録申請などが不要でした。
(本番環境で実際に使えるようにするには以下の手順に従って登録を行います)
https://developers.google.com/pay/api/web/overview

お支払いボタンをクリックすると、

「アプリを認識出来ません。続行する前に、信頼できるアプリかどうか確認してください。」

という画面が新規に表示されますが、「続行」をクリックすることで課金通信出来ます。

Stripeダッシュボード

stripe.comにアクセスして支払いを見てみると、Apple Pay、Google Payとも決済が成功していました。

テスト環境でここまで動作確認出来ていれば本番切り替えも後少しですね。何より決済を実装する為のコーディング環境が手に入ったのが収穫です。

まとめ

Stripeを使うとiPhone、Androidでも同一コードでWeb上の課金処理を実装することが出来ました。

決済代行料3.6%は掛かるけど、PCI-DSS準拠の厳しと比較すれば魅力的な割合と判断出来るのではないでしょうか。

スマホを財布替わりに使って決済する機会が増える中、ニーズのあるサービスを作ることが出来ればチャリンチャリンも夢ではありませんね。

-android, iOS, java, javascript, 決済
-, ,

執筆者:

関連記事

JavaコアAPI数の遷移をChart.jsでグラフ化

Javaはデフォルトで多数のAPIクラスを持っていてコーディングを楽にしてくれます。その数は増え続けているんでしょうか。 バージョンが上がるごとにどう変化しているか調べてchart.jsでグラフ化しま …

Maven環境別ビルド時、プロファイルの違いでdependencyを変える

MavenのResourceFilteringを使い、production、staging、developmentとかで環境別ビルドしている時、ある環境ビルドの時だけ特定のライブラリを追加する、をmv …

B2BスマホアプリをGooglePlay、AppStoreに公開することを安易にお勧め出来ない7つの理由とその対策

商材の性質やシーンに応じてスマホアプリをGooglePlayやAppleStoreのようなストアに公開することがマイナスに働くこともあります。 商材として価値の有る電子データをお持ちの商社さんとアプリ …

JavaとPerlでTCPソケット通信

目次1 はじめに2 対象読者3 実装3.1 TCPサーバ(Perl)3.2 TCPクライアント(Java)3.3 実行4 まとめ はじめに 前回の続きです。One IT ThingJavaとPerlで …

優秀なJava開発者になる為の10のステップ

プログラミングで生計を立てるなら企業ニーズの多いJavaはリターンの多い言語ですから使えるようになっておいて損はありません。 Javaはこの先まだまだ隆盛を誇るでしょうから、優秀なJava開発者になれ …

 

shingo.nakanishi
 

東京在勤、1977年生まれ、IT職歴2n年、生涯現役技術者を目指しています。健康第一。