Stripe ✕ Lambdaでサーバーレスな決済機能を作る

#ちょっとした決済機能
クレジットカードを使った決済機能の実装はハードルが高いイメージがあったのですが、StripeとLambdaを使えばとても簡単にできました。

#決済フローのイメージ
Stripeでは、クレジットカード情報を自分達が預かること無く決済処理を行えます。
![リソース構成図（縦長）.001.png](https://qiita-image-store.s3.amazonaws.com/0/86468/dd304746-45b2-c542-381d-18d8bc69d09b.png)

#APIGatewayとLambdaのセットアップ
予め、APIGatewayからLambdaを実行できるようにしておきます。

- CROSを有効化しておく
- 統合リクエストの本文マッピングテンプレートに「メソッドのパススルー」を設定しておく

その上で、JavaScirptのSDKをコンソール上で生成してダウンロードしておきます。今回はonetime-paymentというリソースのPOSTメソッドでLambdaを実行します。
![スクリーンショット 2018-03-06 16.43.08.png](https://qiita-image-store.s3.amazonaws.com/0/86468/b36a7c65-1317-9105-a449-65456cb7ccb5.png)

#フロント側
ディレクトリ構造

- index.html
- js
    - apigClient.js ：APIGatewayで生成したもの
    - lib :APIGatewayで生成したもの

```html:index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Stripe Sample</title>
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <style type="text/css">
        #payment-form {
            padding: 30px;
            background-color: #f7f8f9;
        }
        #card-errors {
            margin-top: 16px;
        }
        .StripeElement {
            background-color: white;
            padding: 10px 12px;
            border-radius: 4px;
            border: 1px solid #ced4da;
            -webkit-transition: box-shadow 150ms ease;
            transition: box-shadow 150ms ease;
        }
        .StripeElement--focus {
            border-color: #80bdff;
        }
        .StripeElement--invalid {
            border-color: #fa755a;
        }
        .StripeElement--webkit-autofill {
            background-color: #fefde5 !important;
        }
    </style>

</head>
<body>

    <div class="container">

        <form id="payment-form">
            <div class="row">
                <div class="col-sm-8">
                    <div id="card-element"></div>
                    <div id="card-errors" class="alert alert-warning" role="alert" style="display: none;"></div>
                </div>
                <div class="col-sm-4">
                    <button class="btn btn-primary btn-block">支払いを実行する</button>
                </div>
            </div>
        </form>

    </div>

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>

    <!-- Bootstrap -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>

    <!-- API Gateway SDK -->
    <script type="text/javascript" src="js/lib/axios/dist/axios.standalone.js"></script>
    <script type="text/javascript" src="js/lib/CryptoJS/rollups/hmac-sha256.js"></script>
    <script type="text/javascript" src="js/lib/CryptoJS/rollups/sha256.js"></script>
    <script type="text/javascript" src="js/lib/CryptoJS/components/hmac.js"></script>
    <script type="text/javascript" src="js/lib/CryptoJS/components/enc-base64.js"></script>
    <script type="text/javascript" src="js/lib/url-template/url-template.js"></script>
    <script type="text/javascript" src="js/lib/apiGatewayCore/sigV4Client.js"></script>
    <script type="text/javascript" src="js/lib/apiGatewayCore/apiGatewayClient.js"></script>
    <script type="text/javascript" src="js/lib/apiGatewayCore/simpleHttpClient.js"></script>
    <script type="text/javascript" src="js/lib/apiGatewayCore/utils.js"></script>
    <script type="text/javascript" src="js/apigClient.js"></script>

    <!-- stripe.js -->
    <script src="https://js.stripe.com/v3/"></script>

    <script type="text/javascript">
        'use strict';

        // Create a Stripe client.
        const stripe = Stripe('公開可能な方のAPIキー');
        // Create an instance of Elements.
        const elements = stripe.elements();
        // Create an instance of the card Element.
        const card = elements.create('card');

        $(function(){

            // Add an instance of the card Element into the `card-element` <div>.
            card.mount('#card-element');

            // Handle real-time validation errors from the card Element.
            card.addEventListener('change', function(event) {
                const displayError = $('#card-errors');
                if (event.error) {
                    displayError.text(event.error.message);
                    displayError.show();
                } else {
                    displayError.hide();
                }
            });
        });

        // Handle form submission.
        $('#payment-form').submit(function(event){
            event.preventDefault();

            stripe.createToken(card).then(function(result) {
                if (result.error) {
                    // Inform the user if there was an error.
                    const errorElement = $('#card-errors');
                    errorElement.text(result.error.message);
                    errorElement.show();
                } else {
                    // Send the token to your server.
                    errorElement.hide();
                    stripeTokenHandler(result.token);
                }
            });

        });

        function stripeTokenHandler(token){
            const apigClient = apigClientFactory.newClient({region: 'ap-northeast-1'});
            apigClient.onetimePaymentPost({}, {token: token.id}, {})
            .then(function(data){
                console.log(data);
                //決済成功時の処理
            })
            .catch(function(err){
                console.log(err);
                //決済失敗時の処理
            });
        }

    </script>
</body>
</html>
```

#Lambda側

`npm install stripe`でライブラリを導入し、index.jsを下記のようにしてZIPファイルにしてアップロード

```javascript:index.js
'use strict';
const stripe = require("stripe")("シークレットAPIキー");//シークレットキーでインスタンス生成

exports.handler = (event, context, callback) => {

    const token = event['body-json'].token;

    stripe.charges.create({
        amount: 999,//999円で決済
        currency: "jpy",
        description: "Example charge",
        source: token,
    }, function(err, charge) {
        if(err){
            callback(err);
        }else{
            callback(null, charge);
        }
    });

};

```