Webpay(stripe)でトークン決済とかJSONPとかクロスドメインリクエスト

この記事では、実際にRails + Webpayのトークン決済を導入する方法を説明しながら、

裏側で使われている技術とかを紹介しますよ.

## トークン決済！？（；゜０゜）

そもそもトークン決済て何。みたいな話はやりはwebpayのドキュメントを見るのが一番分かりやすそう

https://webpay.jp/docs/payments_with_token


## 早速、動くコード（最小の構成）

動くコードを見るのが一番早いと思います.

説明はその後します.

例は短いに超した事がないと思ってるので最小構成で作ってみた.

### 最小の構成とは
フォームにカード情報を入力してもらい、`ItemsController` の `buy`アクションを叩く事を想定しましょうか.

この`ItemsController#buy` の中でフォームから受け取ったカード情報で決済をゴニョゴニョしたい感じです.


* view１つ（test.html.erb）
* controllerのaction１つ（items_controller.rb#buy）

この２つがあればおk


```html:test.html.erb
<script type="text/javascript" src="https://js.webpay.jp/v1/"></script>
<script type="text/javascript">

  // token生成のAPIを叩いた結果(statusとresponse)を引数として受け取る（コールバック）関数を定義する
  var webpayResponseHandler = function(status, response) {
    var form = $("#payment-form");
    if (response.error) {
      // token生成失敗後の処理を書く
    } else {
      // token生成成功後の処理を書く
      // この例では取得したtokenを<input type="hidden">にのせてappendしてる
      var token = response.id;
      var input = document.createElement("input");
      input.type = "hidden";
      input.name = "token";
      input.value = token;
      
      $(input).appendTo(form);

      form.get(0).submit();
    }
  };

  $(document).ready(function() {
    // フォームの送信がボタンが押されると..
    $("#payment-form").submit(function(event) {
      // Webpayで発行した公開鍵を入れましょう（商用、テスト環境はよしなに）
      WebPay.setPublishableKey("test_public_19DdUs78k2lV8PO8ZCaYX3JT");
      // こいつがAPIを叩きにいってる、上で定義したコールバック関数もご一緒に
      WebPay.createToken({
        number:    $("#card-number input").val(),
        name:      $("#card-name input").val(),
        cvc:       $("#card-cvc input").val(),
        exp_month: $("#card-expiry-month input").val(),
        exp_year:  $("#card-expiry-year input").val()
      }, webpayResponseHandler);
      return false;
    });
  });

 </script>

<%= form_tag buy_items_path, id: "payment-form" do %>

  <div id="card-number">カード番号: <%= text_field_tag "card[number]", "4242-4242-4242-4242"%></div>

  <div id="card-name"> 名義: <%= text_field_tag "card[name]", "KENGO HAMASAKI" %></div>

  <div id="card-cvc">セキュリティコード: <%= text_field_tag "card[cvc]", "123" %></div>

  <div id="card-expiry-month">
    有効期限(月): <%= text_field_tag "card[exp_month]", "12" %>
  </div>

  <div id="card-expiry-year">
    有効期限(年): <%= text_field_tag "card[exp_year]", "2015" %>
  </div>

  <%= submit_tag '送信' %>

<% end %>


```


```ruby:items_controller.rb
class ItemsController < ApplicationController
  def buy
    binding.pry
    params[:token]
    # [大事]!!!↑ここにトークンが入ってる!!!
  end
end


```

### 実際に動かしてみた
Gyazo使ってみたかったので使ってみた

![2a4ac8b2c16dd12f70e7f327a4bd7869.gif](https://qiita-image-store.s3.amazonaws.com/0/30440/79c9768f-536a-ea04-a8b2-91c5282d0643.gif)
(controller名とかはちょっと違うけどあんまり気にしないで）


### ファッ！？（；゜０゜）

formにあるはずのない、params[:tokens]が！？

このトークンがカード情報（番号、CSV、有効期限、氏名）と等価に扱えるので、

僕たちはカード番号やCVCなどの取り扱いの面倒な情報を持たなくておk

トークン決済スゲー

## 以下説明

### 1.カード情報が入力されたformのsubmitボタンが押される

### 2.$("#payment-form").submit()が発火
このイベント発火後の処理の中で、
WebPayオブジェクト(https://js.webpay.jp/v1/ 内で定義されてる）に必要な情報を渡しAPIを叩く


### 3.webpayResponseHandlerがAPIの結果を受け取り処理
これは同一生成元ポリシーの元で許されていないクロスドメインリクエストですが、以下に説明するとJSONPというハックを使って実現している.

クライアントとクロスドメイン先のサーバーであらかじめ取り決めたコールバック関数を叩くというものだ.

今回はwebpayResponseHandlerが引数にHTTP(S)のステータスコードとレスポンスを取る。

今回の例では受け取ったtokenをhiddenタグとしてform内に仕込みsubmitし直している。

=====ここまで読めば動くものは作れる======

## JSONP

JSONPを知らない方は、まあググって適当に調べてもらうと良い.

今回は、https://js.webpay.jp/v1/ 内で上記の機構をどう実現してるか書きますよ.

### 以下疲れたのでちょっと手抜き

Webpay.createTokenで必要な情報を受け取った後、

```js:
callbackId = "jsonp_" + new Date * 1;
```

の様にユニークなcallbackIdを作ります。

```js:
window[callbackId] = function (data, status) {
  window.clearTimeout(timeoutId);
  return webpayResponseHandler(status, data)
};
```

windowオブジェクトに対してこれをキーにコールバック関数をセットします。


```js:
script = document.createElement("script");

script.src = this.endpoint + option.path + "?" + toParam(option.data) + "&callback=" + callbackId;

document.getElementsByTagName("body")[0].appendChild(script)
```

srcの部分に叩きたいAPIのendpointとParamsをつめhtml内にappendします.

appendされたscriptタグが評価されるとAPIを叩きにいきます.

```json:
jsonp_1398067755529({"id":"tok_5Cb4bo3LidO6dDn","object":"token","livemode":false,"created":1398067774,"used":false,"card":{"object":"card","exp_year":2015,"exp_month":12,"fingerprint":"215b5b2fe460809b8bb90bae6eeac0e0e0987bd7","name":"KENGO HAMASAKI","country":"JP","type":"Visa","cvc_check":"pass","last4":"4242"}}, 201) 
```

`callbackId` という関数の呼び出し文が返されます.

callbackId(data,status)という形式になってます.

先ほどのwindowオブジェクトに埋め込んだ関数の引数としてこの(data,status)が入ってきます.