ECサイト運営開発記

PHPフレームワーク Laravelの使い方を中心とした通販サイトの開発日記

Stripe APIで決済手数料を取得する方法

Stripe APIで決済(Charge)の手数料の取得方法がわからず、四苦八苦していましたが、Balance Transactionで取得するらしいので、
その方法を残しておくことにする。

nohohox.hateblo.jp

  • BalanceTransaction APIでsourceにCharge IDを渡してあげればいいだけ。
<?php

  // キーの設定
  \Stripe\Stripe::setApiKey(Config::get("services.stripe.secret"));

  $balanceTransaction=\Stripe\BalanceTransaction::all(
    ["source"=>"ch_19***************"]
  );

  dd($balanceTransaction);

  • 結果

f:id:nohohon_x:20170120142204p:plain


シンプルにChargeから取得できれば言うことないんですが、ちょっと回りくどい印象。
他に方法はなさそうなのがちょっと残念。

Laraveを使ってStripeの決済を導入する

Laravelで利用可能なインターネット決済APIの「Stripe」を試してみたので、基本的な利用方法をまとめておく。

f:id:nohohon_x:20161029025137p:plain

「Stripeの特徴」

  • 月額固定料金なし
  • 1取引あたり3.6%の手数料
  • JavaScriptのコードを貼り付けるだけで、ネット決済システムを導入可能
  • 定期支払も可能。
  • PHPRubyPythonJavaなど様々な言語やプラットフォームに対応
  • Apple Payにも対応
  • シンプルでわかりやすいAPIが用意されている
  • APIから決済成立/全額払い戻す/一部返金など様々な操作が可能

アカウントの作成

アカウントの作成はメールアドレスとパスワードの登録だけで可能。テスト環境はそれだけで用意ができます。

Stripe: Register


テスト環境のAPIキーの確認

StripeのAPIにアクセスするために、ダッシュボードの右上のアカウント情報からAPIキーを確認しておきましょう。Laravelでは、基本的にSecretKeyのみを使います。
f:id:nohohon_x:20161029030528p:plain


テスト環境と本番環境のAPIキーが表示されている。今回はテスト環境のAPIを使用。
f:id:nohohon_x:20161029030522p:plain


Laravelで環境をセットアップ

以下、Laravelは5.3を前提として解説します。

  • composer.jsonに"laravel/cashier": "~7.0"を追加して、composer update。
  • config/app.phpのServiceProviderにLaravel\Cashier\CashierServiceProviderを追加
<?php

    'providers' => [
      /* 中略*/
      Laravel\Cashier\CashierServiceProvider::class,
    ],

  • config/services.phpのstripeの項目にテスト用のSecret Keyを設定する。.envに記述してもOK。
<?php

    'stripe' => [
        'model' => App\User::class,
        'secret' => env('STRIPE_SECRET'),
    ],
  • 今回はモデルには決済機能を組み込まないのでスルー。

Stripe APIを使ってカードのトークンを作り、決済手続きをする。(エラーチェックは省きます)

  • /routes/web.phpにサンプルコードを記述
    • 手順はシンプル
      • 入力フォムから受け取ったカード番号、有効年、有効月、セキュリティコードをAPIに送信すると、クレジットカードのトークンが作成される
        • 作ったトークンを元に、決済オブジェクトの作成を行う。基本的にはこれで手続きは終わり。とても簡単。
        • amountは金額、captureをfalseにしておくと、仮売上の状態。デフォルトはtrueの実売上。
<?php

  <form class="card-block" method="post" action="{{URL::to("/payment")}}">
    <div class="card">
      <div class="card-header">カード情報入力</div>
      <div class="card-block">
        <fieldset>
          <legend>CARD INFO</legend>
          <div class="form-group">
            <label>NAME</label>
            <input type="text" class="form-control" name="name" placeholder="YOUR NAME" value="PIKO TAROU">
          </div>
          <div class="form-group">
            <label>NUMBER</label>
            <input type="text" class="form-control" name="number" placeholder="カード番号" value="4242424242424242" >
          </div>
          <div class="form-group">
            <label>EXPIRE</label>
            <div class="row">
              <div class="col-lg-3">
                <input type="number" class="form-control" name="exp_month" placeholder="" value="10">
              </div>
              <div class="col-lg-3">
                <input type="number" class="form-control" name="exp_year" placeholder="" value="2020">
              </div>
            </div>
          </div>
          <div class="form-group">
            <label>CVC</label>
            <input type="number" class="form-control" name="cvc" placeholder="***" value="123">
          </div>
        </fieldset>
        <fieldset>
          <legend>CHARGE OPTIONS</legend>
          <div class="form-group">
            <label>PRICE</label>
            <input type="text" class="form-control" name="amount" placeholder="金額" value="1000" >
          </div>
          <div class="form-group">
            <label>DESRIPTION</label>
            <input type="text" class="form-control" name="description" placeholder="説明文" value="hogehoge" >
          </div>
        </fieldset>
      </div>
      <div class="card-footer">
        <input type="submit" class="btn btn-block btn-danger" value="決済">
      </div>
    </div>
  </form>
<?php

Route::post("/payment",function(Request $request){

  // キーの設定
  \Stripe\Stripe::setApiKey(Config::get("services.stripe.secret"));

  // トークンを作る
  $token=\Stripe\Token::create(array(
    "card" =>  [
      "number" => $request->input("number"),
      "exp_month" => $request->input("exp_month"),
      "exp_year" => $request->input("exp_year"),
      "cvc" => $request->input("cvc"),
      "name" => $request->input("name")
    ]
  ));


  // 決済
  $charge=\Stripe\Charge::create(array(
    "amount" => $request->input("amount"),
    "currency" => "jpy",
    "source" => $token,
    "description" => $request->input("description"),
    "capture"=>false
  ));

  // dd($charge);

  return back();
}
  • 決済オブジェクト一覧を取得する
<?php

Route::post("/index",function(Request $request){

  // キーの設定
  \Stripe\Stripe::setApiKey(Config::get("services.stripe.secret"));

  // 決済一覧を50件取得
  $chargeList=\Stripe\Charge::all(array("limit" =>50));

  return view("stripe/index",["chargeList"=>$chargeList]);

}
  • 決済オブジェクト一覧をbladeのViewで表示する
<?php


      @forelse ($chargeList->data as $key => $value)
        <tr>
          <td>
            <a href="{{URL::to("/view/".$value->id)}}">{{$value->id}}</a>
          </td>
          <td>
            {{$value->source->name}}
          </td>
          <td class="text-xs-center">&yen;{{$value->amount}}</td>
          <td class="text-xs-center">{{$value->source->brand}}</td>
          <td class="text-xs-center">
            @if($value->refunded==true)
              払い戻し済み
            @else
              @if($value->captured)
                実売上
              @else
                仮売上
              @endif
            @endif
          </td>
          <td class="text-xs-center">
            @if($value->refunded==false)
              @if($value->captured==false)
                <a href="{{URL::to("/capture/".$value->id)}}">実売上にする</a>
                /
              @endif
              <a href="{{URL::to("/refund/".$value->id)}}">払い戻す</a>
            @endif
          </td>
        </tr>
      @empty
      @endforelse

  • 決済を仮売上から実売上にする
<?php

Route::any("capture/{id}",function($id){

  // キーの設定
  \Stripe\Stripe::setApiKey(Config::get("services.stripe.secret"));

  // 実売上に変更
  $ch = \Stripe\Charge::retrieve($id);
  $ch->capture();

  return back();

});
  • 決済を全額払い戻し
<?php

Route::any("refund/{id}",function($id){

  // キーの設定
  \Stripe\Stripe::setApiKey(Config::get("services.stripe.secret"));

  // 払い戻し
  $re = \Stripe\Refund::create(array(
    "charge" => $id
  ));

  return back();

});
  • 決済を一部払い戻し
<?php

Route::post("refund-amount/",function(Request $request){

  // キーの設定
  \Stripe\Stripe::setApiKey(Config::get("services.stripe.secret"));

  // 払い戻し
  $re = \Stripe\Refund::create(array(
    "charge" => $request->input("id"),
    "amount" =>$request->input("amount",0),
  ));

  return back();

});


申し込みからテストまで簡単にできるので、Web決済に悩んでいる方はStripeはとてもオススメです。
ただし、日本語ドキュメントはまだ揃っていないことに加え、ダッシュボードもまだ英語なので、そこはWebPayに軍配が上がる感じです。

stripe.com

祖師ヶ谷大蔵の麻婆麺 あかずきん

祖師ヶ谷大蔵に麻婆麺が食べられるお店が出来たらしいので、小田急祖師ヶ谷大蔵駅まで遠出。新宿から各駅停車に乗って12駅、意外と疲れる距離。

 

「あかずきんの外観」

f:id:nohohon_x:20161007133144j:plain

 

 店の入口にある券売機で麻婆麺の食券を購入して店員さんに渡すと、辛さを選べます。1~3まで選べて、1辛はカレーの中辛よりちょっと辛い程度だそうです。で、2辛を頼むことに。見た目は超激辛なビジュアル。しかし、麻婆豆腐自体は甘めでとろみのあるスープでやみつきになる美味さ。辛さが物足りない場合は、卓上の胡椒、山椒、花椒などを入れて辛さと刺激を調節することができるので、2辛でも問題ないかも。中本でいうと、蒙古卵麺くらいの辛さ?

追加料金で追い飯も可能らしいので、ミニ麻婆飯も食べられるので、大食漢の人も満足できる。

f:id:nohohon_x:20161007132033j:plain

麻婆麺はスープが醤油系のスープで薄味になりがちな店が多い中、この店はとろみのある濃厚スープなので、中毒性も高くまた来たくなる味だった。場所が祖師ヶ谷大蔵なのでちょっと遠いのが難点。次食べるときは、激辛に挑んでみたい。

 

関連ランキング:中華麺(その他) | 祖師ケ谷大蔵駅成城学園前駅

買うとキメていたはずのZenbook3ががっかりな値段

ASUSの日本法人がZenbook3を発表。6月に台湾で発表されてから、4ヶ月近く経過し、いつ日本で発売されるのか待ちに待って、ようやく、9月28日に日本国内での発売が正式に発表された。

しかし、その内容はZenfone3の価格と同様に少々残念な結果となった。一体、どこが残念なのか?

12インチMacbookの対抗馬として

まずは簡単なスペック

  • OS:Windows 10 Home 64ビット
  • CPU:Intel Core i5-7200U / Core i7-7500U
  • メモリ:8GB/16GB
  • ストレージ:SSD256GB/512GB
  • ディスプレイ:1,920×1,080ドット (フルHD) /グレア液晶 /12.5型ワイドTFTカラースクリーン ワイドビュー液晶
  • グラフィックチップ:Intel  HD グラフィックス 620
  • 重量:910g
  • バッテリー駆動時間:8.7時間
  • 接続端子:USBポートとイヤフォンジャックが一つずつ
  • その他:指紋センサ搭載

 

f:id:nohohon_x:20160929173757j:plain

 Zenbook3は12インチの筐体でありながら910gと軽量かつ薄型のノートパソコン。ライバル機種はもちろん、アップルの12インチのMacbookだ。しかし、12インチMacbookがCPUにCore mプロセッサを採用しているのに、対して、ASUSはCore i 5/7 シリーズというパワフルなCPUを採用。メモリはMacbookが8GBで統一されているのに対して、Zenbook3は8GBと16GBのどちらかを選択でき、こちらも素晴らしい点。こうした基本スペック以外にも、指紋センサを使用して指一本でロックを解除できたり、急速充電ができる点などASUSの本気度が感じ取れるノートパソコンになっている。

 

しかし、問題は発売時期と価格。

 

日本国内で展開されるモデルは2種類で、下位モデルは139,800円(税別)。これはMacbookの下位モデルが148,800円(税別)だという事を考えると、一見お安く感じる。おまけにMacbookはCore mなのに対して、Zenbook3Core i 5なのだから、性能も上。そう考えれば確かにお安いかもしれないが、いくら性能がいいとはいえ、139,800円(税別)という表記は買うのを躊躇させる値段。税込み価格だと15万円を超えてしまうわけだから。

 

また、シンプルに軽量ノートパソコンを欲しい身としては、12インチのサブマシンにそこまで求めていない。なんなら、同じCore Mを搭載して静音ファンレス設計にした上で、890gという超軽量級のマシンにした上で価格を110,000円(税別)くらいにしてくれた方が、こちらとしては有難かったくらいだ。おまけに現状、バッテリー駆動時間は9時間持たないが、Core Mにしてしまえば10時間は超えるするはず。

 

また、発売のタイミングも若干遅すぎた感が否めない。アップルは年内(一部報道では10月中)にMacbook AirMacbook Proを刷新する可能性があり、この内容によってはZenbook3は一気に忘れ去られてしまう可能性すらある。だったら、そうなる前にさっさと発表してさっさと売ってしまった方がASUS的には得だったのではないか?

 

Macbook Air/Proの新型の噂が燻ってる現在の状況を考えると、昨日の発表内容は「まだ様子を見ておこう」「即買いはやめておこう」という客に思わせてしまったのではないだろうか?なんというか客が一気に引いていった感じ。とっても魅力的な製品なだけに残念な発表内容だった。

 

www.asus.com

では、今回のZenbook3の価格にがっかりした人で軽量ノートパソコンを探している人はどうすればいいか?

選択肢1 .HP EliteBook Folio G1

pc.watch.impress.co.jp

HP EliteBook Folio G1 製品詳細・スペック - ノートパソコン・PC通販 | 日本HP

Zenbook3と同じ12.5インチのディスプレイでフルHD液晶のHPのノートパソコン。重量はZenbook3よりやや重めの980g。Zenbook3よりやや重いとはいえ、1kg切ってるのは素晴らしい。それに加えて、180度開閉できるヒンジを採用していて、膝の上やうつ伏せでながらパソコンも可能。CPUはCore Mシリーズを搭載した静音PC。価格は99,800円(税別)から。ただし、HP直販で購入する必要がある。

選択肢2.Yogabook Windows

http://shopap.lenovo.com/jp/tablets/lenovo/yoga/yoga-book/

www.itmedia.co.jp

10月に日本国内でも発売予定のLenovoのYogabookのWindows版は量販店でも購入可能。RAMは4GBでSSDも64GB、CPUはAtom搭載で性能的には完全にタブレット。しかし、ながら、フラットな板に美しく浮き上がるキーボード「Haloキーボード」を搭載し、360度開閉も可能。10.1インチで重さ690gで持ち運びにもかなり便利。価格も52,800円(税別)でお求めやすい上に、もう7000円足せばLTEモデルも購入可能。SuperFishなどのネガティブなイメージに目を瞑むる事ができれば、意外と良い製品かもしれない。

選択肢3.Macbook AirとProの新型を待つ

www.itmedia.co.jp

まだ噂レベルで、そもそも出るのか出ないのかさっぱりわからない。ただ、Macbook Proに関してはタッチパーが搭載されるという具体的な情報も出てきているので、年内に発売されるのを期待しつつ、新情報を待つほかない。

 

GMOのVPSサービスConoHaが素晴らしい

GMOのConoHaを使ってみてると、これが以外と使い勝手がいい。

www.conoha.jp

以下、ConoHaオススメのポイント

  • シンプルで簡単な管理画面。もちろん日本語。
  • 仮想サーバーをすぐに立ち上げられる。
  • データ転送量の課金がない。無制限。
  • 負荷分散も簡単。
  • メールサーバーも簡単に立ち上げられ、利用料金も安い。月額500円から。
  • メールアドレスの作成も簡単。
  • MongoDBやRedisなどのキャッシュサーバーやbaseCMSやconcreteのようなCMSHadoop、Dockerなども管理画面から簡単に立ち上げられる。
  • クレジットカードがなくても利用できる。料金をチャージして、毎月そこから支払われる。
  • 明瞭会計で使いすぎの不安がない。
  • オブジェクトストレージも用意されている
  • APIも公開

等など、素晴らしい点が多数。しかしながら、以前電源トラブルか何かで大規模障害を起こしたというのも事実。この辺は二度とないようにしていただきたいです。

とはいえ、AWSで小規模サイトを運営している人は乗り換えてみても、損することはないと思います。

Surface Pro3/4で3本指仮想デスクトップの切り替えが可能に。

Windows 10 Anniversary updateで待望の3本指による仮想デスクトップの切り替えが可能になった。ブラウザ←→エディタ間をスムーズに切り替える事が可能になるので、これによって、作業効率がアップするはず。

設定方法は 「設定」→「タッチパッドで検索」→「マウスとタッチパッド」の下の方のある項目群で設定が可能に。他のノートPCで設定可能かどうかは不明だが、Surface Pro3/4では変更が可能。

f:id:nohohon_x:20160809020605j:plain

AngularJS のng-optionsのvalue値でデータ型が表示されてしまったら

AngulrJSでng-optionsを使ってると、value値にデータ型も一緒に表示されてしまい、期待通りの動きになってくれなかった。

  • http requestで取得したJSONデータ
    • 取得したJSONデータは$scope.itemListにセット。
[
  {"item_id":"ITM000000001","item_name":"バタークリームサンド"},
  {"item_id":"ITM000000002","item_name":"ワッフルクッキー"},
  {"item_id":"ITM000000003","item_name":"マカロン"},
  {"item_id":"ITM000000004","item_name":"ココナッツ クッキー"}
]
<select ng-model="selectedItem" ng-options="item.item_name for item in itemList"></select>

しかし、このコードだと出力されるHTMLは以下の様に、データ型も一緒に表示される上、空のデータに対してselectedが割り当てられてしまう。

<select ng-model="selectedItem" ng-options="item.item_name for item in itemList">
<option value="?" selected="selected"></option>
<option value="string:ITM000000001" label="バタークリームサンド" selected="selected">バタークリームサンド</option>
<option value="string:ITM000000002" label="ワッフルクッキー">ワッフルクッキー</option>
<option value="string:ITM000000003" label="マカロン">マカロン</option>
<option value="string:ITM000000004" label="ココナッツ クッキー">ココナッツ クッキー</option>
</select>

しかし、string:というデータ型が邪魔である。
これをどうやって解決するのか?

  • 解決策その1
    • ng-optionsをやめて、ng-repeatを使う。
<select ng-model="selectedItem">
<option ng-repeat="item.item_name for item in itemList" value="{{item.item_id}}">{{item.item_name}}</li>
</select>
  • 解決策その2
    • track by句を使う
      • 重複した配列を出力する際に使われるキーワードらしいですが、この句を使うと、データ型を省略した文字列で表示されます。どういう理屈かはわかりません。
<select ng-model="selectedItem" ng-options="item.item_name for item in itemList track by item.item_id"></select>
  • 空のselectedの解決策
    • JSONを取得した段階でselectedItemにitemList[0]を指定しておく。またはng-initにて、初期値を指定。


出力されるHTML。

<select ng-model="selectedItem" ng-options="item.item_name for item in itemList track by item.item_id">
<option value="ITM000000001" label="バタークリームサンド" selected="selected">バタークリームサンド</option>
<option value="ITM000000002" label="ワッフルクッキー">ワッフルクッキー</option>
<option value="ITM000000003" label="マカロン">マカロン</option>
<option value="ITM000000004" label="ココナッツ クッキー">ココナッツ クッキー</option>
</select>

AngularJSは高機能なだけに、使いこなすのが難しいです。