Slide 1

Slide 1 text

Stripe Appsではじめる サービス連携・UIカスタマイズ JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 @hidetaka_dev June 2022

Slide 2

Slide 2 text

2 #Stripe #JP_Stripes https://marketplace.stripe.com/

Slide 3

Slide 3 text

2022/06/18時点でStripe AppsはPublic Betaです。 システムや機能が大きく変わる可能性があることに ご了承ください。

Slide 4

Slide 4 text

Stripe Apps: Stripeを拡張し、運営を効率化するツール 4 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 運用の効率化 ● 複雑な決済処理フローを簡素化 ● Stripe APIを組み合わせたタスク ● 外部サービスと連携した自動化 ● 独自WF用のPrivateアプリ開発 ツール間のコンテキスト共有 ● Stripeとツールでデータをリンク ● より詳細なビジネス分析 ● CRMでの顧客管理 ● 会計データの自動連携 Why use Stripe Apps? Now available in beta

Slide 5

Slide 5 text

Stripe Apps活用方法は3種類(現時点ではAのみ) A. Privateアプリを開発し、自社で必要なデータ連携や ワークフローの自動化効率化を実現する B. Marketplaceのアプリを組み合わせ、 ノーコードで業務の効率化を実現する C. Marketplaceにアプリを公開し、 自社サービスの顧客獲得やネイティブ課金での収益化を実現する 5 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 6

Slide 6 text

機能 Marketplace公開済みアプリの例 会計業務効率化 Xero, Bench マーケティング・CRM・顧客サポート Intercom, Mailchimp, SendOwl コンテンツ・コラボレーション・スケジューリング DocuSign, Dropbox, Nylas 金融業務 Ramp, FundBox 分析 Baremetrics, Chartmogul Climate Persefoni, Climatiq, Vaayu Others (e.g. 税務, データ連携 Exemptax.com, Render Appでできること(例) Stripe Apps 6

Slide 7

Slide 7 text

マーケットプレイスからアプリをインストール 7 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 8

Slide 8 text

インストール時に権限確認(& アカウント連携) 8 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 9

Slide 9 text

セットアップが完了すると、ダッシュボードに表示 9 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 10

Slide 10 text

[設定 > インストール済みアプリ]からアプリ管理 10 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 11

Slide 11 text

アプリのUIはページ毎に設定可能 ● ダッシュボードのページ毎に 表示内容が変更可能 ( Viewport ) ● 開発時に表示させたいViewportを指定する ○ stripe.dashboard.customer.detail ○ stripe.dashboard.drawer.default ○ settings ● ページ毎に固有のコンテキストがある ○ 顧客詳細ページ: customer id ○ 商品詳細ページ: product id ○ etc… 11 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門

Slide 12

Slide 12 text

Stripe Apps開発入門

Slide 13

Slide 13 text

開発ドキュメント: stripe.com/docs/stripe-apps 13 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 14

Slide 14 text

Stripe CLIからセットアップ 14 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 # v1.8.11以前の場合はアップグレード $ stripe version # Apps開発用のStripeアカウントにログイン $ stripe login # Apps用のプラグインをインストール $ stripe plugin install apps # アプリセットアップ $ stripe apps create helloworld

Slide 15

Slide 15 text

セットアップ時にアプリIDと表示名を設定 ● アプリIDはグローバルで一意 ○ 重複が発生すると、 apps uploadでのアップロードが エラーになるケースも ○ 重複しにくいIDを設定しよう ● 表示名・IDどちらも後から変更可能 ○ stripe-app.json ○ 混乱を避けるため、 IDはupload後からは変更しない ● npmまたはyarnを使って プロジェクトセットアップ ○ PCに未インストールの場合は インストールしてリトライ 15 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 🖊 Enter details of your new Stripe app (these can be changed later) ✔ App ID: com.example.helloworld-1█ ✔ Display name: Helloworld 1█ ✔ Created skeleton files ✔ Created default view ✔ Wrote package.json yarn add v1.22.17 info No lockfile found. [1/5] Validating package.json... [2/5] Resolving packages...

Slide 16

Slide 16 text

Stripe CLIでローカル開発を開始 16 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 $ stripe apps start [18:17:37] compiled successfully 💻 Running development server on http://localhost:4242 (^C to quit) ✨ You can now preview Helloworld 1 in your Stripe account. 🌐 Press Enter to open the Stripe dashboard ● デフォルトでは、顧客詳細ページのみ ● 初回起動時は確認画面が表示される

Slide 17

Slide 17 text

Appsの実装はReact & TypeScript ● 専用のUIコンポーネントのみを使う ○ @stripe/ui-extension-sdk/ui ○ HTMLタグなどはエラーに ○ CSSはinlineで定義 ○ React Nativeに近い ● ページ毎にエントリーポイント ○ 1ファイル1ページ ○ default exportした要素が親に ○ stripe-app.jsonで設定を確認 ● 3つのViewコンポーネントを親に設定 ○ ContextView: 標準 ○ FocusView:  モーダル的要素 ○ SettingsView: 設定ページ 17 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 import { Box, ContextView, Inline, Link } from "@stripe/ui-extension-sdk/ui"; import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context"; const App = ({ userContext, environment }: ExtensionContextValue) => { return ( Edit{" "} src/views/App.tsx{" "} and save to reload this view. …

Slide 18

Slide 18 text

利用できるUI要素はドキュメント参照 18 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 19

Slide 19 text

ページの追加はstripe apps add ● ダッシュボードのページID指定か 設定ページかを選べる ● Viewを選んだ場合は、 追加したいページのviewport idを指定 ● 追加すると、stripe-app.jsonが更新される ○ ui_extensions.viewが増える ○ viewportがviewportのID ○ componentが読み込むファイル名 ● Viewportの一覧と詳細 https://stripe.com/docs/stripe-apps/refere nce/viewports  19 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 $ stripe apps add Use the arrow keys to navigate: ↓ ↑ → ← ? Select an entry point: ▸ view settings ? Select a viewport to go into: ▸ stripe.dashboard.payment.list stripe.dashboard.payment.detail stripe.dashboard.customer.list stripe.dashboard.customer.detail stripe.dashboard.invoice.list stripe.dashboard.invoice.detail ✔ Enter view name (e.g. ExampleComponentName): PaymentListView█

Slide 20

Slide 20 text

React Hookでデータ処理を実装 ● useState / useMemo / useEffect useCallback + React Context / etc.. ● UIコンポーネントのonイベントや useEffectなどでイベントを発火 ● 注意点(一部) ○ Refは現状未サポート ○ Reactはバージョンv17 ○ 環境変数設定不可 ● 実装の詳細や注意点 https://stripe.com/docs/stripe-apps/how-u i-extensions-work   20 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 export const usePaymentLinkDetail = ( environment: ExtensionContextValue["environment"] ) => { const paymentLinkId = useMemo(() => { if (environment.objectContext.object !== "payment_link") return null; return environment.objectContext.id || null; }, [environment]); const [paymentLink, setPaymentLink] = useState(null); useEffect(() => { if (!paymentLinkId) return; stripeClient.paymentLinks.retrieve(paymentLinkId) .then((link) => setPaymentLink(link)) }, [setPaymentLink, paymentLinkId]); return { paymentLink, loadPaymentLinkDetail }; };

Slide 21

Slide 21 text

AppsでStripe SDK(node)が利用可能 ● サーバー側アプリなしで Stripe APIが呼び出せる ● Stripeクラスの初期化時に ui-extension-sdk/http_clientの ヘルパーを利用 ● データ連携機能を組み込みたい場合、 このSDKでWebhook管理するAPIを利用 https://stripe.com/docs/stripe-apps/build- backend#receiving-events-webhooks  ● 事前に権限設定が必要なことに注意 https://stripe.com/docs/stripe-apps/refere nce/permissions  21 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 import Stripe from "stripe"; import { createHttpClient, STRIPE_API_KEY, } from "@stripe/ui-extension-sdk/http_client"; export const stripeClient = new Stripe(STRIPE_API_KEY, { httpClient: createHttpClient() as Stripe.HttpClient, apiVersion: "2020-08-27", }); #設定 stripe apps grant permission "PERMISSION_NAME" "EXPLANATION" #解除 stripe apps revoke permission "PERMISSION_NAME"

Slide 22

Slide 22 text

外部API呼び出しは、事前登録と署名チェックが必要 ● Stripe API以外のAPIを呼ぶには、 ホワイトリストへの登録が必須 ○ stripe apps grant url ○ URLと用途を登録 ● ワイルドカード(*)はサブドメインのみ ● 動的なパスを持つ場合、 「https://example.com/api/」のように 「/」で終わる形で登録する ● 実装詳細: https://stripe.com/docs/stripe-apps/buil d-ui#use-third-party-apis 22 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 22 $ stripe apps grant url ✔ Enter a connect-src URL (to call a third-party API from your app): https://e Enter a connect-src URL (to call a third-party API from your app): https://example.com/api ✔ Enter a purpose for the grant: Processing subscription event in our SaaS ser Enter a purpose for the grant: Processing subscription event in our SaaS server ✔ Granted url https://example.com/api

Slide 23

Slide 23 text

署名チェックはフロント・サーバー両方に実装 23 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 const stripe = require('stripe')(process.env.STRIPE_API_KEY); // Find your app's secret in your app settings page in the Developers Dashboard. const appSecret = 'absec_...'; app.post('/do_secret_stuff', (request, response) => { const sig = request.headers['stripe-signature']; // Retrieve user id and account id from the request body const payload = JSON.stringify({ user_id: request.body['user_id'], account_id: request.body['account_id'] }); try { // Verify the payload and signature from the request with the app secret. stripe.webhooks.signature.verifyHeader(payload, sig, appSecret); } catch (error) { response.status(400).send(error.message); } import fetchStripeSignature from '@stripe/ui-extension-sdk/signature'; const App = ({ userContext, environment }) => { const makeRequestToMyBackend = async (endpoint, requestData) => { // By default the signature is signed with user id and account id. const signaturePayload = { user_id: userContext?.id, account_id: userContext?.account.id, }; return fetch(`https://example.com/${endpoint}/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(), 'Content-Type': 'application/json', }, body: JSON.stringify({ ...requestData, ...signaturePayload, }), }); }; ... }

Slide 24

Slide 24 text

外部サービスとの連携は、原則OAuth 2.0 PKCE ● Privateアプリやローカルで試す分には、 APIキー認証でも動作します ○ Marketplaceに公開する場合は、 OAuth 2.0 PKCEを利用 ● createOAuthStateや Apps用のリダイレクトURLで実装 ● 認証成功後のアクセストークンは StripeのSecret Store APIに保存 ● 実装詳細: https://stripe.com/docs/stripe-apps/oau th 24 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 import {createOAuthState} from '@stripe/ui-extension-sdk/oauth'; import type {ExtensionContextValue} from '@stripe/ui-extension-sdk/context'; const clientID = 'your_client_id'; const getRedirectURL = (mode: 'live' | 'test') => `https://dashboard.stripe.com/${ mode === 'test' ? 'test/' : ''}apps-oauth/com.example.oauth-example`; const getAuthURL = (state: string, challenge: string, mode: 'live' | 'test') => `https://www.example.com/oauth2/authorize?response_type=code&client_id=${clientID}&re direct_uri=${getRedirectURL(mode)}&state=${state}&code_challenge=${challenge}&code_ch allenge_method=S256`; const ExampleApp = ({environment}: ExtensionContextValue) => { const {mode} = environment; const [authURL, setAuthURL] = useState(''); useEffect(() => { createOAuthState().then(({state, challenge}) => { setAuthURL(getAuthURL(state, challenge, mode)); }); }, [mode]);

Slide 25

Slide 25 text

Stripe Apps開発のおさらい ● 基本的にはReact + TypeScriptアプリ ○ UIコンポーネントやCSS・環境変数などに制約あり ○ Stripe SDK(node)がそのまま使える ○ ユニットテストはJest ● 1View 1ファイルのNext.jsに近いスタイル ○ Viewの追加や変更はCLIとstripe-app.json ● 外部APIやサービス連携も可能 ○ ただしホワイトリストの登録やOAuth 2.0での連携などが必要 25 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門 #JP_Stripes

Slide 26

Slide 26 text

サンプルコード: https://github.com/stripe/stripe-apps/tree/main/examples ● 開発の参考になるサンプルアプリ群 ○ TODO ○ Webhookインストール ○ OAuth ○ FocusView / SettingsView ○ etc.. 26 JP_Stripes in サッポロ Vol.7 Stripe再入門 & アプリ開発入門

Slide 27

Slide 27 text

ビデオチュートリアル(英語) 27 JP_Stripes DeepDive 2022/05 https://www.youtube.com/watch?v=0Ha5NRIs49o

Slide 28

Slide 28 text

Discord: #stripe-apps-beta-help 28 JP_Stripes DeepDive 2022/05 https://discord.com/channels/841573134531821608/974592073229549638

Slide 29

Slide 29 text

日本語チュートリアル in Qiita 29 JP_Stripes DeepDive 2022/05 https://qiita.com/hideokamoto/items/0a167387d046e154d2cf

Slide 30

Slide 30 text

今後のロードマップ(一部) 30 JP_Stripes DeepDive 2022/05