Slide 1

Slide 1 text

AWS LambdaとStripeで オンライン決済・定期課金APIを実装しよう PHPerKaigi 2022 Hidetaka Okamoto (@hide__dev) 2022/04/11

Slide 2

Slide 2 text

岡本 秀高 ( @hide__dev ) ● Stripe Developer Advocate (ex-developer in Digitalcube) ● JavaScript / TypeScript developer ● AWS / Next.js / WordPress / etc… ● WordCamp Kyoto 2017 / JP_Stripes Connect 2019 / AWS Samurai 2017 / etc… ● 🐈(猫フラご容赦󰢛) 2 PHPerKaigi 2022 #phperkaigi

Slide 3

Slide 3 text

● Serverless Frameworkで AWS LambdaにPHPアプリをデプロイする ● PHPで Stripeの決済フォームを実装する ● AWS Secrets Managerで Stripe APIキーを安全に管理する ● Webhook APIを実装・デプロイする ● 定期課金を実装する際のTips Agenda PHPerKaigi 2022

Slide 4

Slide 4 text

● Serverless Frameworkで AWS LambdaにPHPアプリをデプロイする ● PHPで Stripeの決済フォームを実装する ● AWS Secrets Managerで Stripe APIキーを安全に管理する ● Webhook APIを実装・デプロイする ● 定期課金を実装する際のTips Agenda PHPerKaigi 2022

Slide 5

Slide 5 text

AWS LambdaでPHPアプリを実行する ● デフォルトのランタイムにPHPは存在しない ● 「カスタムランタイム」を作成し、 Lambdaにアップロードする ● カスタムランタイムは、DockerfileまたはLambda Layerで設定可能 ● 自作または公開されている Layerを利用するスタイルになる 5 PHPerKaigi 2022

Slide 6

Slide 6 text

自作する場合の Dockerfileの例 PHPerKaigi 2022 From: https://aws.amazon.com/jp/builders-flash/202106/new-lamb da-container-development-3/?awsf.filter-name=*all

Slide 7

Slide 7 text

Serverless Framework + brefで手軽にPHP環境 ● AWS Lambda向けの Serverless Frameworkプラグイン ● PHPアプリを 簡単(Bref)にデプロイ・実行できる ● Composerを利用して インストール ● YAMLや PHPのテンプレートも提供 7 PHPerKaigi 2022 #phperkaigi https://bref.sh/

Slide 8

Slide 8 text

Serverless FWスタックのセットアップ 8 PHPerKaigi 2022 % composer init % composer require bref/bref % vendor/bin/bref init What kind of lambda do you want to create? (you will be able to add more functions later by editing `serverless.yml`) [Web application]: [0] Web application [1] Event-driven function > 0 Creating index.php Creating serverless.yml [OK] Project initialized and ready to test or deploy.

Slide 9

Slide 9 text

“Web application”でのYAMLファイル(一部) 9 PHPerKaigi 2022 provider: name: aws region: us-east-1 runtime: provided.al2 plugins: - ./vendor/bref/bref functions: api: handler: index.php layers: - ${bref:layer.php-81-fpm} events: - httpApi: '*' …

Slide 10

Slide 10 text

serverless deployでデプロイ 10 PHPerKaigi 2022 $ serverless deploy Deploying app to stage dev (us-east-1) ✔ Service deployed to stack app-dev (98s) endpoint: ANY - https://xx.execute-api.us-east-1.amazonaws.com functions: api: app-dev-api (1.4 MB)

Slide 11

Slide 11 text

● Serverless Frameworkで AWS LambdaにPHPアプリをデプロイする ● PHPで Stripeの決済フォームを実装する ● AWS Secrets Managerで Stripe APIキーを安全に管理する ● Webhook APIを実装・デプロイする ● 定期課金を実装する際のTips Agenda PHPerKaigi 2022

Slide 12

Slide 12 text

ライブラリのインストールなどもいつも通り 12 PHPerKaigi 2022 % composer require stripe/stripe-php $_ENV['STRIPE_SECRET_API_KEY'], 'stripe_version' => '2020-08-27', ]); % composer require stripe/stripe-php

Slide 13

Slide 13 text

環境変数は.env -> serverless.ymlで設定 13 PHPerKaigi 2022 % composer require stripe/stripe-php useDotenv: true … functions: api: … environment: STRIPE_SECRET_API_KEY: ${env:STRIPE_SECRET_API_KEY} STRIPE_PUBLISHABLE_API_KEY: ${env:STRIPE_PUBLISHABLE_API_KEY} STRIPE_SECRET_API_KEY=sk_test_xxx STRIPE_PUBLISHABLE_API_KEY=pk_test_xxx .env serverless.yml

Slide 14

Slide 14 text

PaymentIntentをPHPで作成 14 PHPerKaigi 2022 $_ENV['STRIPE_SECRET_API_KEY'], 'stripe_version' => '2020-08-27', ]); $paymentIntent = $stripe->paymentIntents->create([ 'payment_method_types' => ['card', ‘konbini’], 'amount' => 1009, 'currency' => 'jpy', ]); ?>

Slide 15

Slide 15 text

Stripe.jsで決済フォームを描画 15 PHPerKaigi 2022
document.addEventListener('DOMContentLoaded', async () => { const stripe = Stripe('<?php echo $_ENV["STRIPE_PUBLISHABLE_API_KEY"]; ?>', { apiVersion: '2020-08-27', }); const elements = stripe.elements({ clientSecret: "<?php echo $paymentIntent->client_secret; ?>" }); const paymentElement = elements.create("payment"); paymentElement.mount('#card-element'); });

Slide 16

Slide 16 text

複数決済方法に対応した 決済フォームが完成 PHPerKaigi 2022

Slide 17

Slide 17 text

● Serverless Frameworkで AWS LambdaにPHPアプリをデプロイする ● PHPで Stripeの決済フォームを実装する ● AWS Secrets Managerで Stripe APIキーを安全に管理する ● Webhook APIを実装・デプロイする ● 定期課金を実装する際のTips Agenda PHPerKaigi 2022

Slide 18

Slide 18 text

StripeのシークレットAPIキーを安全に管理する ● シークレットAPIキーは、顧客情報や決済履歴などの重要なデータにアクセスできる ● より安全に管理するために、 AWSのSecrets Managerを利用 ● Secrets Managerで集約管理することで、退職者の開発 PCにキーが残るリスクもなくせる ● コストが気になる場合は、 Systems Managerまたは制限付きAPIキーの代用も検討可能 18 PHPerKaigi 2022

Slide 19

Slide 19 text

AWS SDK(PHP)でシークレットAPIキーを取得する 19 PHPerKaigi 2022 use Aws\SecretsManager\SecretsManagerClient; $client = new SecretsManagerClient([ 'profile' => 'default', 'version' => '2017-10-17', ]); $result = $client->getSecretValue([ 'SecretId' => '<<{{MySecretName}}>>', ]); if (isset($result['SecretString'])) { $secret = $result['SecretString']; } else { $secret = base64_decode($result['SecretBinary']); } $stripe = new \Stripe\StripeClient([ 'api_key' => $secret['STRIPE_SECRET_API_KEY'], 'stripe_version' => '2020-08-27', ]);

Slide 20

Slide 20 text

● Serverless Frameworkで AWS LambdaにPHPアプリをデプロイする ● PHPで Stripeの決済フォームを実装する ● AWS Secrets Managerで Stripe APIキーを安全に管理する ● Webhook APIを実装・デプロイする ● 定期課金を実装する際のTips Agenda PHPerKaigi 2022

Slide 21

Slide 21 text

コンビニなど、 即時決済でない支払いも Stripeではサポート 21 PHPerKaigi 2022

Slide 22

Slide 22 text

決済イベントを Webhookで処理 PHPerKaigi 2022 https://stripe.com/docs/webhooks/quickstart

Slide 23

Slide 23 text

serverless.ymlでWebhook用のリソースを追加 23 PHPerKaigi 2022 functions: webhook: handler: webhook.php description: '' timeout: 28 layers: - ${bref:layer.php-81-fpm} events: - httpApi: method: POST path: '/webhook'

Slide 24

Slide 24 text

JSONを返すAPIを実装 24 PHPerKaigi 2022 ‘OK', ], JSON_PRETTY_PRINT);

Slide 25

Slide 25 text

Stripeからのリクエストであることを検証する処理の例 25 PHPerKaigi 2022

Slide 26

Slide 26 text

実装の詳細は Stripe docs参照 PHPerKaigi 2022 https://stripe.com/docs/webhooks/quickstart

Slide 27

Slide 27 text

● Serverless Frameworkで AWS LambdaにPHPアプリをデプロイする ● PHPで Stripeの決済フォームを実装する ● AWS Secrets Managerで Stripe APIキーを安全に管理する ● Webhook APIを実装・デプロイする ● 定期課金を実装する際の Tips Agenda PHPerKaigi 2022

Slide 28

Slide 28 text

契約と決済情報登録が同時の場合、 先にSubscriptionを作成する 28 PHPerKaigi 2022 % composer require stripe/stripe-php const stripe = Stripe(  '', { 'apiVersion: '2020-08-27', }); const elements = stripe.elements({ clientSecret: "latest_invoice->payment_intent->client_secret; ?>" }); const paymentElement = elements.create("payment"); paymentElement.mount('#card-element'); $subscription = $stripe->subscriptions->create([ 'customer' => $customer_id, 'items' => [[ 'price' => $price_id, ]], 'payment_behavior' => 'default_incomplete', 'expand' => ['latest_invoice.payment_intent'], ]); Subscriptionを作成して・・・ 決済フォームにclient secretを渡す

Slide 29

Slide 29 text

Webhookを利用して、支払い方法をサブスクに設定 29 PHPerKaigi 2022 paymentIntents->retrieve( $payment_intent_id, [] ); $stripe->subscriptions->update( $subscription_id, ['default_payment_method' => $payment_intent->payment_method], ); };

Slide 30

Slide 30 text

実装を簡略化したい場合、Checkoutでローコードに 30 PHPerKaigi 2022 'https://example.com/success.html?session_id={CHECKOUT_SESSION_ID}', 'cancel_url' => 'https://example.com/canceled.html', 'mode' => 'subscription', 'line_items' => [[ 'price' => $priceId, 'quantity' => 1, ]], ]); header("HTTP/1.1 303 See Other"); header("Location: " . $session->url);

Slide 31

Slide 31 text

もしくはSetupIntentで先にカード情報を登録する ステップを用意する 31 PHPerKaigi 2022 % composer require stripe/stripe-php const stripe = Stripe(  '', { 'apiVersion: '2020-08-27', }); const elements = stripe.elements({ clientSecret: "client_secret; ?>" }); const paymentElement = elements.create("payment"); paymentElement.mount('#card-element'); $setupIntent = $stripe->setupIntents->create([ 'customer' => $customerId, ]); SetupIntentを作成して・・・ 決済フォームにclient secretを渡す

Slide 32

Slide 32 text

まとめ ● Serverless Framework + BrefでPHPアプリをAWSにデプロイできる ● Stripeなど、composerを使った組み込みも可能 ● Stripeを利用する場合、WebPage(HTML)だけでなくREST APIも作ろう ● ローカルでのテストや、画像などの最適化は Brefのドキュメントをチェック ● LaravelやSymfonyのデプロイにもBrefは対応 ● AWS + Stripeで、より手軽により少ないメンテナンス工数で アプリケーション・サービスをリリースしよう 32 PHPerKaigi 2022