Stripe調査(クレジットカード・オンライン決済)

オンライン決済サービスを調査。

## どのサービスを使うかという点

何個かのサービスを比べてみた

- stripe
- paypal
- Gmoペイメントゲートウェイ
- Pay.jp


料金についてGMOは決済手数料以外に固定費がかかるので厳しい(サポートは厚そう)。
基本はstripeとpaypal中心で比較。
料金もどちらも基本無料。決済額に応じてn%(3%前後)。
対応カードも主要な所はOK。StripeがApplePay、GooglePayにも対応している。
利用開始の手軽さは間違いなくstripe。paypalはサービスの開始はすぐ出来るが、引き落とし可能になるまで審査などがある。セキュリティなどの信頼はpaypalか。
ただしpaypalは利用者がアカウントを作成する必要があり、ユーザーとしてはハードルが高そう。
開発が柔軟そうなのはstripeか。（Stripe はデベロッパー・ファーストを掲げ、開発着手の容易さをウリの一つにしている。とのこと）

そもそも、paypalアカウントを作らないと行けないというのが一ハードルが高そう。
Pay.jpは手数料低いし、日本の会社だからドキュメントも日本語で助かるがー...規模的に一旦除外。(サービス終了が怖い)
なので、取り敢えずStripeとする。

[参考リンク]
stripe・paypal比較
https://web.analogstd.com/tips/posts/web/compare-paypal-stripe.php
https://commerce-media.info/blogs/ec/shopify-paypal
https://n10shop.com/post-49191/

2021クレジットカード決済比較
https://notepm.jp/blog/2937

OMISEとPAY.JPの比較
https://www.puti-banbeena.com/cgi/2018/10/24/omise_or_payjp/

## Stripe機能調査

###取り敢えず簡単に動かす

CheckoutというStripe側に遷移して行う機能あり。これで良いなぁと思う。
https://stripe.com/jp/payments/checkout
https://stripe.com/docs/checkout/integration-builder
https://qiita.com/piggydev/items/c70630b5194406ca0e88



### 遷移はしない。cardという人まとまりのフォームを生成するか、一つずつ個別にフォームを作成する。

Stripe Elementsを使う。入力フォームごとにオブジェクトを生成してHTML上にmountするという使い方。
cardというオブジェクトでまとめて一つのフォームを作成出来る。
cardNumber、MM、 YY、 CVCなど個別に作成することも出来る。

カスタマイズパターン例
https://stripe.dev/elements-examples/

### そもそもStripe Elementsも使わないパターン

Number, exp_month, exp_year、cvcを以下APIにでcard_tokenを取得することが出来る。
https://stripe.com/docs/api/tokens/create_card
https://stripe.com/docs/api/tokens

このtokenで決済出来る。わかりやすい。フォームのカスタマイズを想定するなら、これが一番やりやすそう。


### カード情報を保存したい

stripeはこれも出来る。ありがたや。アプリ側で持つのではなくて、Stripeに保存出来る。
あとでカード番号などの編集はできない。編集する場合は削除＋追加という流れになる。

customerを作成してそのsourceにcard_tokenをセットするという流れ。
以後は、customerをから、関連するsource情報を取得する。
下４桁は表示出来る。



## ちょっと用語の確認

- charge　= 売り切りの決済オブジェクト。chargeをcreateで決済する
- subscription = 継続課金のオブジェクト。subscriptionをcreateして決済する。次回以降は勝手に決済してくれる
- customer = 顧客オブジェクト。適当に作成出来る。アプリ側にcustomer_idを持って、そのidでstripeから各データを取得したり、決済するのが基本的な使い方で良さそう。
- source = カード情報など決済情報はsourceに保存する。customerオブジェクト必須。


## もうちょっと理解

そもそもStripeでの決済のフロー (わかりやすかった)
https://qiita.com/y_toku/items/7e51ef7e69d7cbbfb3ca

APIリファレンス
https://stripe.com/docs/api

Rubyでの各決済関連処理の実装例 (参考になった)
https://qiita.com/tomokazu0112/items/89f69c47761ac782ce13
⇒subscriptionの登録はサービスプランとなっていて情報が古そう、シンプルにリファレンスのやり方でできた。詳細は後述。
https://stripe.com/docs/api/subscriptions/create


## ここまで苦労した所
- customerを取得するときに、カード情報や設定しているsubscriptionを取得するときexpandしないと取れない場合がある。リファレンスでのそのプロパティにEXPANDABLEとあったら以下のようにexpandで項目名を指定する。

```
    customer = Stripe::Customer.retrieve({
      id:@@custmer_id,
      expand:['sources'], 
    })
```

- chekcoutとかEelemnts使うと簡単にフォームは生成出来る。入力チェックとかもしっかりしているし。
- でもchekcoutとかEelemntsとかの前に取り敢えず、「card情報を入力してSDKなり、APIでTokenを生成して、そのTokenを使って決済が出来る」と理解しておけば良さそう。無理してSDK使わんで良い。
- Elementsを使うと、サンプルではcardオブジェクトを引数にtokenを作成するが、cardNumberオブジェクトを引数にしても出来る。
- テストモードのAPIキーで実行した決済などでは、領収書の自動メールは送信されない。
- token生成APIにリクエストするときに、application/jsonではエラーになり、application/x-www-form-urlencodedでないと上手くいかなかった。そのためjsでfetchする場合も以下リンクのようにqueryを作らないとエラーになってしまう....https://bigbinary.com/blog/using-stripe-api-in-react-native-with-fetch


# サブスクリプション決済関連　詳細調査

サブスクリプションの決済について、もうちょっと調査実施

customerをexpandしてsubscriptionを取得すると、activeなものだけ取得出来る。
cancelしたものも取得したければ 以下のようにstatsuで調整する。

```
    Stripe::Subscription.list(
      {
        customer: custmer_id,
        status: :all
      }
    )

```


### cancelについて

- APIにて、subscription.deleteを実行すると即時キャンセルとなり、statusがcanceledとなる。
- キャンセル時に、購入済みの期間が終わるまで継続という場合は、cancel_at_period_endをtrueにして対応する。

```
 stripe.subscriptions.update(subscriptionId, {
    cancel_at_period_end: true
})
```

https://www.366service.com/jp/qa/730d74e478b36566a472e3d5a08a936b
https://wp-kyoto.net/cancel-stripe-billing/

prorateについてはまだ良くわから無い...


### 返金について

取り敢えず管理画面から、任意の金額で返金は出来る。便利ー。
ただし、手数料は戻らないので注意。


## stripeとアプリのデータ同期まわり

stripeでイベントが発生したときに、すぐにアプリとデータ連携したい場合は、webhook機能がある。
それをトリガーとしてアプリ側で更新などを行うことになる。
https://stripe.com/docs/webhooks

選択可能なイベントは管理画面で選択する所で確認出来る。


例として "customer.subscription.deleted"の場合以 下のようなデータが送信される。dataとして削除されたsubscriptionオブジェクトが渡されている。

```
{
  "created": 1326853478,
  "livemode": false,
  "id": "evt_00000000000000",
  "type": "customer.subscription.deleted",
  "object": "event",
  "request": null,
  "pending_webhooks": 1,
  "api_version": "2020-08-27",
  "data": {
    "object": {
      "id": "sub_00000000000000",
      "object": "subscription",
      "application_fee_percent": null,
      "billing_cycle_anchor": 1610505452,
      "billing_thresholds": null,
      "cancel_at": null,
      "cancel_at_period_end": false,
      "canceled_at": 1610934767,
      "collection_method": "charge_automatically",
      "created": 1610505452,
      "current_period_end": 1610937452,
      "current_period_start": 1610851052,
      "customer": "cus_00000000000000",
      "days_until_due": null,
      "default_payment_method": "pm_1I8zElDFW27RwAFON0UF6nQz",
      "default_source": null,
      "default_tax_rates": [
      ],
      "discount": null,
      "ended_at": 1610948851,
      "items": {
        "object": "list",
        "data": [
          {
            "id": "si_00000000000000",
            "object": "subscription_item",
            "billing_thresholds": null,
            "created": 1610505453,
            "metadata": {
            },
            "price": {
              "id": "price_00000000000000",
              "object": "price",
              "active": true,
              "billing_scheme": "per_unit",
              "created": 1610505247,
              "currency": "jpy",
              "livemode": false,
              "lookup_key": null,
              "metadata": {
              },
              "nickname": null,
              "product": "prod_00000000000000",
              "recurring": {
                "aggregate_usage": null,
                "interval": "day",
                "interval_count": 1,
                "usage_type": "licensed"
              },
              "tiers_mode": null,
              "transform_quantity": null,
              "type": "recurring",
              "unit_amount": 100,
              "unit_amount_decimal": "100"
            },
            "quantity": 1,
            "subscription": "sub_00000000000000",
            "tax_rates": [
            ]
          }
        ],
        "has_more": false,
        "url": "/v1/subscription_items?subscription=sub_IkUD7xWhwqBRyv"
      },
      "latest_invoice": "in_1IAR9uDFW27RwAFOYiA3UjEB",
      "livemode": false,
      "metadata": {
      },
      "next_pending_invoice_item_invoice": null,
      "pause_collection": null,
      "pending_invoice_item_interval": null,
      "pending_setup_intent": null,
      "pending_update": null,
      "schedule": null,
      "start_date": 1610505452,
      "status": "canceled",
      "transfer_data": null,
      "trial_end": null,
      "trial_start": null
    }
  }
}
```