Laravel5.6 へ Stripe.com の checkout を導入する

<前準備>

1.composer.jsonに以下を追記

"require": {
"php": ">=7.0.0",
"fideloper/proxy": "~3.3",
"laravel/framework": "5.5.*",
"laravel/tinker": "~1.0",
"laravelcollective/html": "^5.5",
"league/flysystem-aws-s3-v3": "~1.0",
"stripe/stripe-php": "6.*" ←☆ここを追記
},

 

2.SSHでlaravelのインストールディレクトリに移動し、以下を実行

$ composer update

 

3.vendorディレクトリの下に、「stripe」が出来ていればインストール成功

 

<プログラム>

https://stripe.com/docs/quickstart

 このページを見ると、Stripeには、大きく、

●Checkout

●Stripe.js and Elements

の2つの方法があることがわかる。

カスタマイズが必要な場合は、Stripe.jsを使うようだが、とりあえず、Checkoutで進める。

 

<決済画面の作成>

stripe.comのCheckoutのページをみると、以下のコードを貼ると、ポップアップで決済画面が表示されるとのことであった。

https://stripe.com/docs/checkout#integration-simple

<form action="your-server-side-code" method="POST">
  <script
    src="https://checkout.stripe.com/checkout.js" class="stripe-button"
    data-key="pk_test_******************"
    data-amount="999"
    data-name="Demo Site"
    data-description="Widget"
    data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
    data-locale="auto"
    data-currency="jpy">
  </script>

 

しかし、何度やっても表示されない。

どうやら、Laraveのテンプレ(app.blade.php)に標準で入っている『app.js』

<script src="{{ asset('js/app.js') }}"></script>

があると、Stripeのコードが動かないことがわかった。

 

よって、【シンプル】コードは諦め、同じページにある【Custom】タブにあるコードを貼ると、うまくポップアップするようになった。

https://stripe.com/docs/checkout#integration-custom

<script src="https://checkout.stripe.com/checkout.js"></script>

<button id="customButton">Purchase</button>

<script>
var handler = StripeCheckout.configure({
  key: 'pk_test_***********************',
  image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
  locale: 'auto',
  token: function(token) {
    // You can access the token ID with `token.id`.
    // Get the token ID to your server-side code for use.
  }
});

document.getElementById('customButton').addEventListener('click', function(e) {
  // Open Checkout with further options:
  handler.open({
    name: 'Demo Site',
    description: '2 widgets',
    currency: 'jpy',
    amount: 2000
  });
  e.preventDefault();
});

// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
  handler.close();
});
</script>

 

上記サンプルプログラムでは、カード情報のトークン化完了後、決済処理に移る方法が書かれていない。そこで、トークンが返ってきた後、フォームをコントローラーに送信するために、以下のような感じにした。

【Viewのソース】(Formで遷移バージョン)

 
<form id="stripecharge" method="post"
action="{{ asset('orderexec') }}" class="form-horizontal">
{{ csrf_field() }}
 
<button id="customButton" class="stripe_button">注文画面</button>
<input type="hidden" id="stripeToken" name="stripeToken" />
<input type="hidden" id="stripeEmail" name="stripeEmail" />
<input type="hidden" id="stripeArgs" name="stripeArgs" />
</form>
 
<script>
var handler = StripeCheckout.configure({
key: '{{ env('STRIPE_PUB_KEY') }}',
currency: 'jpy',
locale: 'auto',
zipCode: true,
name: '{{ config('app.name', 'Laravel') }}',
description: '2 widgets',
billingAddress: true,
shippingAddress: true,
email: "{{ $email }}",
token: function(token, args) {
   $("#stripeToken").val(token.id);
   $("#stripeEmail").val(token.email);
   $("#stripeArgs").val(JSON.stringify(args));
   $("#stripecharge").submit();
}
});

$("#customButton").on('click', function(e) {
var final_price = $("#changed_total_cost").text();
final_price = final_price.replace(/\$/g, '').replace(/\,/g, '');
final_price = parseFloat(final_price);

// Open Checkout with further options:
handler.open({
   amount: final_price,
});
e.preventDefault();
});

// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
 

 

決済金額は、オプションやら送料やらで変動するため、$("#customButton").on('click')の中で決済金額を決定した後、

handler.open({ })

の中で、amountを決定している。

https://stripe.com/docs/recipes/variable-amount-checkout

 

上記Formで遷移バージョンの場合、やってみるとわかるが、stripeからトークン処理が返ってきた後、$("#stripecharge").submit() でフォームをPOSTするのだが、その間の処理が微妙に長く(数秒)、ユーザーは「あれ?」と思ってしまう。

よって、ajaxで処理し、途中はローディング画像を流し、決済完了後はモーダル画面を表示させるようにした。

【(改良版)Viewのソース】(ajaxバージョン)

 
<button id="customButton" class="stripe_button">ご注文画面</button>
{{!! 決済完了後に表示させるモーダルウィンドウの準備!!}}
<div class="remodal" data-remodal-id="modal_ordercomp"
data-remodal-options="hashTracking:false, closeOnOutsideClick: true">
    <h1>ご注文完了!</h1>
    <p>ご注文が完了しました。</p>
<button data-remodal-action="close" class="remodal-close"></button>
</div>

<script>
var handler = StripeCheckout.configure({
key: '{{ env('STRIPE_PUB_KEY') }}',
currency: 'jpy',
locale: 'auto',
zipCode: true,
name: '{{ config('app.name', 'Laravel') }}',
description: '2 widgets',
billingAddress: true,
shippingAddress: true,
email: "{{ $email }}",
token: function(token, args) {
   $("#kurukuru").css('display','block');←☆ローディングの表示
   $.ajaxSetup({
      headers: {
         'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
      }
  });
   $.ajax({
      type: "POST",
      dataType: "text",
      data: {'stripeToken': token.id,
      'stripeEmail':token.email,
      'stripeArgs': JSON.stringify(args),
      },
       url: "{{ asset('mypage/mybooks/orderexec') }}",
   }).done(function(data){
      //決済完了後、モーダルウィンドウを表示させる
      //以下は、remodal.jsとの連携例。適当に変えてください。
      $('[data-remodal-id=modal_ordercomp]').remodal().open();
  }).fail(function(res){
      alert('通信エラーが発生しました。');
   });
}
});

$("#customButton").on('click', function(e) {
var final_price = $("#changed_total_cost").text();
final_price = final_price.replace(/\$/g, '').replace(/\,/g, '');
final_price = parseFloat(final_price);

// Open Checkout with further options:
handler.open({
   amount: final_price,
});
e.preventDefault();
});
 

 

 <コントローラー側のプログラム>

 コントローラーは人によってする処理が違うので最低限のプログラムだけ下に書いておく。

public function orderexec(Request $request)
{
try {
   Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
   $token = $request->input('stripeToken');
   $stripeArgs = $request->input('stripeArgs');
   logger($stripeArgs);

   $customer = Customer::create([
      'email' => $request->stripeEmail,
      'source' => $token,
      'metadata' => ['user_id' => Auth::id()],
   ]);
   
   $charge = Charge::create([
      'customer' => $customer->id,//必須
      'amount' => 999,
      'currency' => 'jpy',
      'description' => 'Example charge',
      'metadata' => ['order_id' => 6735],
   ]);
}
catch (\Exception $ex) {
   return $ex->getMessage();
}
return;
}
 

 ポイントは以下の2点。

$customer = Customer::create([
      'email' => $request->stripeEmail,
      'source' => $token, ←★ここでStripeから帰ってきたトークンを指定
      'metadata' => ['user_id' => Auth::id()],
   ]);

$charge = Charge::create([
      'customer' => $customer->id,←★customerでトークンを使ったので、このように指定する。再度トークンを指定するとエラーになった。
      'amount' => 999,
      'currency' => 'jpy',
      'description' => 'Example charge',
      'metadata' => ['order_id' => 6735],
   ]);

 

以上、ご参考まで。

(おかしな点があればコメントいただければ助かります。)