【Rails】Stripeの実装（カード情報保存版）

# 環境

- Rails 5.2.3
- [stripe-ruby](https://github.com/stripe/stripe-ruby) 4.21.3

# ドキュメント（Stripe公式）

- [クレジットカード情報の保存](https://stripe.com/docs/saving-cards)

# 設定

## KEYの設定（環境変数）
[こちらを参照。](https://qiita.com/luigiii/items/835f5363fe0f8bd07477#key%E3%81%AE%E8%A8%AD%E5%AE%9A%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0)

## initializersの設定
[こちらを参照。](https://qiita.com/luigiii/items/835f5363fe0f8bd07477#initializers%E3%81%AE%E8%A8%AD%E5%AE%9A)

# コンソールでテスト

テスト結果は、[stripeのダッシュボード](https://dashboard.stripe.com/logs)で確認可能。
※ユーザ固有のキーを使っている場合のみ。
※ダッシュボードで 「テストデータを表示」 をチェックすること。

## リクエスト例
  - amountはドル換算で 50セント以上になるようにする（50セント未満の場合、exceptionが発生する）。
  - tokenは以下の何れかを参照 （※以下の例では`tok_mastercard`を使用している）。
     - https://stripe.com/docs/testing#cards
     - https://stripe.com/docs/testing#international-cards （※`currency`として`jpy`を使う場合はこっち。但し、テストなのでどちらもで良い）

```ruby
## customer の作成
customer = Stripe::Customer.create({
    source: 'tok_mastercard',
    email: 'paying.user@example.com',
})

## customer にチャージする （カードではなく）
charge = Stripe::Charge.create({
    amount: 1000,
    currency: 'jpy',
    customer: customer.id,
})

## customer ID （customer.id） や、その他の情報をデータベースに保存する。

## 再度、customer にチャージする際に、↑で保存した customer ID を使う。以下の customer_id はデータベースに保存していたもの。
charge = Stripe::Charge.create({
    amount: 1500,
    currency: 'jpy',
    customer: customer_id,
})
```

## レスポンス例（↑の例では`customer`に入るもの）

```ruby
> customer.class
=> Stripe::Customer < Stripe::APIResource

> customer.id
=> "cus_FuW98aPiB42PLY"

> customer
=> {
                       :id => "cus_FuW98aPiB42PLY",
                   :object => "customer",
          :account_balance => 0,
                  :address => nil,
                  :balance => 0,
                  :created => 1569920002,
                 :currency => nil,
           :default_source => "card_1FOh6k2eZvKYlo2C86LDl8BE",
               :delinquent => false,
              :description => nil,
                 :discount => nil,
                    :email => "paying.user@example.com",
           :invoice_prefix => "032DE63F",
         :invoice_settings => {
                 :custom_fields => nil,
        :default_payment_method => nil,
                        :footer => nil
    },
                 :livemode => false,
                 :metadata => {},
                     :name => nil,
                    :phone => nil,
        :preferred_locales => [],
                 :shipping => nil,
                  :sources => {
             :object => "list",
               :data => [
            [0] {
                                 :id => "card_1FOh6k2eZvKYlo2C86LDl8BE",
                             :object => "card",
                       :address_city => nil,
                    :address_country => nil,
                      :address_line1 => nil,
                :address_line1_check => nil,
                      :address_line2 => nil,
                      :address_state => nil,
                        :address_zip => nil,
                  :address_zip_check => nil,
                              :brand => "MasterCard",
                            :country => "US",
                           :customer => "cus_FuW98aPiB42PLY",
                          :cvc_check => nil,
                      :dynamic_last4 => nil,
                          :exp_month => 10,
                           :exp_year => 2020,
                        :fingerprint => "7a9bk9ncM08SXfua",
                            :funding => "credit",
                              :last4 => "4444",
                           :metadata => {},
                               :name => nil,
                :tokenization_method => nil
            }
        ],
           :has_more => false,
        :total_count => 1,
                :url => "/v1/customers/cus_FuW98aPiB42PLY/sources"
    },
            :subscriptions => {
             :object => "list",
               :data => [],
           :has_more => false,
        :total_count => 0,
                :url => "/v1/customers/cus_FuW98aPiB42PLY/subscriptions"
    },
               :tax_exempt => "none",
                  :tax_ids => {
             :object => "list",
               :data => [],
           :has_more => false,
        :total_count => 0,
                :url => "/v1/customers/cus_FuW98aPiB42PLY/tax_ids"
    },
                 :tax_info => nil,
    :tax_info_verification => nil
}
```

## レスポンス例（↑の例では`charge`に入るもの）

```ruby
> charge.class
=> Stripe::Charge < Stripe::APIResource

> charge
=> {
                             :id => "ch_1FOh8a2eZvKYlo2CqJvuzqx2",
                         :object => "charge",
                         :amount => 1000,
                :amount_refunded => 0,
                    :application => nil,
                :application_fee => nil,
         :application_fee_amount => nil,
            :balance_transaction => "txn_1FOh8b2eZvKYlo2C0J9XmWSz",
                :billing_details => {
        :address => {
                   :city => nil,
                :country => nil,
                  :line1 => nil,
                  :line2 => nil,
            :postal_code => nil,
                  :state => nil
        },
          :email => nil,
           :name => nil,
          :phone => nil
    },
                       :captured => true,
                        :created => 1569920116,
                       :currency => "jpy",
                       :customer => "cus_FuW98aPiB42PLY",
                    :description => nil,
                    :destination => nil,
                        :dispute => nil,
                   :failure_code => nil,
                :failure_message => nil,
                  :fraud_details => {},
                        :invoice => nil,
                       :livemode => false,
                       :metadata => {},
                   :on_behalf_of => nil,
                          :order => nil,
                        :outcome => {
        :network_status => "approved_by_network",
                :reason => nil,
            :risk_level => "normal",
            :risk_score => 34,
        :seller_message => "Payment complete.",
                  :type => "authorized"
    },
                           :paid => true,
                 :payment_intent => nil,
                 :payment_method => "card_1FOh6k2eZvKYlo2C86LDl8BE",
         :payment_method_details => {
        :card => {
                     :brand => "mastercard",
                    :checks => {
                      :address_line1_check => nil,
                :address_postal_code_check => nil,
                                :cvc_check => nil
            },
                   :country => "US",
                 :exp_month => 10,
                  :exp_year => 2020,
               :fingerprint => "7a9bk9ncM08SXfua",
                   :funding => "credit",
                     :last4 => "4444",
            :three_d_secure => nil,
                    :wallet => nil
        },
        :type => "card"
    },
                  :receipt_email => nil,
                 :receipt_number => nil,
                    :receipt_url => "https://pay.stripe.com/receipts/acct_1032D82eZvKYlo2C/ch_1FOh8a2eZvKYlo2CqJvuzqx2/rcpt_FuWAT1u6h54pfgGz0uOTzqZbV8EgbPX",
                       :refunded => false,
                        :refunds => {
             :object => "list",
               :data => [],
           :has_more => false,
        :total_count => 0,
                :url => "/v1/charges/ch_1FOh8a2eZvKYlo2CqJvuzqx2/refunds"
    },
                         :review => nil,
                       :shipping => nil,
                         :source => {
                         :id => "card_1FOh6k2eZvKYlo2C86LDl8BE",
                     :object => "card",
               :address_city => nil,
            :address_country => nil,
              :address_line1 => nil,
        :address_line1_check => nil,
              :address_line2 => nil,
              :address_state => nil,
                :address_zip => nil,
          :address_zip_check => nil,
                      :brand => "MasterCard",
                    :country => "US",
                   :customer => "cus_FuW98aPiB42PLY",
                  :cvc_check => nil,
              :dynamic_last4 => nil,
                  :exp_month => 10,
                   :exp_year => 2020,
                :fingerprint => "7a9bk9ncM08SXfua",
                    :funding => "credit",
                      :last4 => "4444",
                   :metadata => {},
                       :name => nil,
        :tokenization_method => nil
    },
                :source_transfer => nil,
           :statement_descriptor => nil,
    :statement_descriptor_suffix => nil,
                         :status => "succeeded",
                  :transfer_data => nil,
                 :transfer_group => nil
}
```

# エラー処理

[こちらを参照。](https://qiita.com/luigiii/items/835f5363fe0f8bd07477#%E3%82%A8%E3%83%A9%E3%83%BC%E5%87%A6%E7%90%86)

# 独自Exception

[こちらを参照。](https://qiita.com/luigiii/items/835f5363fe0f8bd07477#%E7%8B%AC%E8%87%AAexception)

# モック

[こちらを参照。](https://qiita.com/luigiii/items/835f5363fe0f8bd07477#%E3%83%A2%E3%83%83%E3%82%AF)

# 実装時の注意点

[こちらを参照。](https://qiita.com/luigiii/items/835f5363fe0f8bd07477#%E5%AE%9F%E8%A3%85%E6%99%82%E3%81%AE%E6%B3%A8%E6%84%8F%E7%82%B9)

# おまけ

フリーランスで活動しています。
開発案件（Go、Rails、Blockchain、機械学習など）をお待ちしております。

[職務経歴書（wantedlyへのリンク）](https://www.wantedly.com/users/36882295)