Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stripe Search APIを利用した、LINEとStripeの顧客情報連携/line-dc-202205

Stripe Search APIを利用した、LINEとStripeの顧客情報連携/line-dc-202205

◆イベント情報
LINE de 決済勉強会!LINE API×Stripeエキスパート秘伝のノウハウを共有!
https://linedevelopercommunity.connpass.com/event/246062/

More Decks by Hidetaka Okamoto (Stripe)

Other Decks in Technology

Transcript

  1. Stripe Search APIを利用した、LINE とStripeの顧客情報連携 LINE de 決済勉強会!LINE API×Stripeエキスパート秘伝のノウハウを共有! @hide__dev May

    2022 1 #LINEDC #JP_Stripes
  2. 岡本 秀高 ( @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 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  3. npm install use-line-liff 3 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #jamjamjamstack import {

    LiffProvider } from 'use-line-liff' <LiffProvider liffId={process.env.NEXT_PUBLIC_LIFF_ID as string} mock={{ enable: true, }} > {() => { const { liff } = useLiff() }} </LiffProvider>
  4. Agenda • Search APIを使わずにLINEのユーザーとStripeのCustomerを連携 • Stripe Search API入門 • Search

    APIを使った顧客情報連携方法 ◦ Checkout編 ◦ Payment Links編 • Search APIを顧客管理やプロモーションに活用する 4 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  5. Agenda • Search APIを使わずにLINEのユーザーとStripeのCustomerを連携 • Stripe Search API入門 • Search

    APIを使った顧客情報連携方法 ◦ Checkout編 ◦ Payment Links編 • Search APIを顧客管理やプロモーションに活用する 5 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  6. LINEとStripeの連携には、DBが必要(だった) • 現状、LINE APIでユーザーにmetadataをつけれない・取得できない • Stripe APIはEmailかIDでしか取得ができなかった ◦ LINEにメールアドレスはないので、実質IDだけ •

    LINEのユーザーIDとStripe Customer IDを紐づけるDBが必要だった ◦ 取得・更新だけなら、RDBMSまでいかずとも、 Amazon DynamoDBやRedisなどのKVS系でOK ◦ GET / PUT操作のみなので、 Alexa SDKのようにAmazon S3にJSON配置でもいけたかも 6 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  7. 余談: Alexaのask-sdkは、データをJSONで管理できる • DynamoDB(KVS)と S3(Objct Storage)が選べる • Key - Value形式のデータを、

    ユーザーIDなどをキーに 取得・更新・削除 • S3は結果整合性なので、 頻繁に更新するケースでは 古いデータが残るリスクがある 7 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 public async saveAttributes(userId: string, attributes: {[key: string]: string}): Promise<void> { const putParams: S3.PutObjectRequest = { Bucket : this.bucketName, Key : userId, Body : JSON.stringify(attributes), }; try { await (new AWS.DynamoDB())).putObject(putParams).promise(); } catch (err) { throw Error(err); ); } } #LINEDC #JP_Stripes
  8. Agenda • Search APIを使わずにLINEのユーザーとStripeのCustomerを連携 • Stripe Search API入門 • Search

    APIを使った顧客情報連携方法 ◦ Checkout編 ◦ Payment Links編 • Search APIを顧客管理やプロモーションに活用する 8 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  9. Stripe Search API (2022春GA) • データを検索するためのAPI • Metadataなどへのクエリや、 AND /

    ORなどを利用した検索に 対応したAPI • 対応リソース: ◦ Payment Intents (支払) ◦ Customers (顧客) ◦ Invoices (請求書) ◦ Prices (料金) ◦ Products (商品) ◦ Subscriptions (サブスク) ◦ etc… 9 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 export const getMonthlyCustomerSucceededPayment = async (customerId) => { const targetMonth = dayjs() const result = await stripe.paymentIntents.search({ query: [ `created>${targetMonth.startOf('month').unix()}`, 'AND', `created<${targetMonth.endOf('month').unix()}`, 'AND', `customer:"${customerId}"`, 'AND', `status:"succeeded"`, 'AND', 'currency:"JPY"', ].join(' '), }) return result } #LINEDC #JP_Stripes
  10. Search APIとLINEの連携例 • LINEのユーザーIDを customers.metadataに保存 • Customerに対するSearchで、 metadataを引っ掛けるクエリを書く • `-field:null`で、

    「NOT NULL」データの検索も可能 • CRMやマーケティング用途で、 「LINEにメッセージを送れる顧客」の 検索も可能 10 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 // 特定のIDをmetadataに持つCustomerを検索 await stripe.customers.search({ query: `metadata['userId']:'${userId}'` }) // IDをmetadataに持つCustomerだけを検索 await stripe.customers.search({ query: `-metadata['userId']:null`, limit: 100, }) #LINEDC #JP_Stripes
  11. Stripe Search APIの注意事項 • ANDとORの併用はエラーになる • API versionが 2020-08-27より古いと使えない •

    データを更新した時、 検索結果に反映されるまで 少しラグがある(通常は 1分未満) • Rate limitに注意 ◦ 20 read operations per second ◦ セールやプロモーションで 顧客が殺到すると・・・ • ユーザー数が増加してきたら、 KVS系DBへの切り替えも要検討 11 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 await stripe.paymentIntents.search({ query: [ `status:"succeeded"`, 'AND', 'currency:"JPY"', 'OR', 'currency:"USD"', ].join(' '), }) // 実行結果 { type: 'StripeInvalidRequestError', raw: { message: 'Queries cannot have a mix of AND and OR.', type: 'invalid_request_error', #LINEDC #JP_Stripes
  12. Agenda • Search APIを使わずにLINEのユーザーとStripeのCustomerを連携 • Stripe Search API入門 • Search

    APIを使った顧客情報連携方法 ◦ Checkout編 ◦ Payment Links編 • Search APIを顧客管理やプロモーションに活用する 12 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  13. Checkoutの注文にLINEのユーザー情報を連携する • Checkoutのセッション作成時に、 Customerを作るのが確実 • Search APIでユーザーが すでに存在するかを確認 • 存在しない場合は、データを作る

    • 取得・生成したCustomer IDを、 Checkoutセッション作成時の引数へ 13 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 const putAndGetStripeCustomerIdByUserId = async (userId) => { const {data} = await stripe.customers.search({ query: `metadata['userId']:'${userId}'` }) if (data.length < 1) { const customer = await stripe.customers.create({ metadata: { userId, } }) return customer.id } return data[0].id } #LINEDC #JP_Stripes
  14. Webhookを使えば、Payment Linksでも • Client_reference_idに LINEのUser IDをセットする • Checkoutのセッション完了時に、 Stripe Customerにデータを登録する

    • Payment Linksの場合、 Customerが複数作成されることに注意 • LIFFなどで コードを書いて実装する場合、 Checkoutの利用を推奨 14 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 liff.init({ liffId: import.meta.env.LIFF_ID, }).then(() => { // ログインしていない if (!liff.isLoggedIn()) { return liff.login() } return liff.getProfile() .then(profile => { window.open(`https://buy.stripe.com/test_xxxxx?client_reference_id=${profile.userI d}`) }) }).catch(e => { console.log(e) }) #LINEDC #JP_Stripes
  15. Webhookを使って「決済後」にIDをセットする例 • Customerの作成を後でやるタイプ • カゴ落ちした顧客の Customerデータが作られない • Payment Linksでの注文時にも、 顧客データを入れたい場合も使える

    • カゴ落ちした顧客に リカバーメッセージなどを 出したい場合は不向き 15 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 if (( ['checkout.session.completed', 'checkout.session.async_payment_succeeded' ].includes(event.type)) && data.payment_status === 'paid' ) { const userId = data.metadata?.user_id if (userId) { // put user id to customer const customer = await stripe.customers.retrieve(data.customer) if (!customer.deleted && !customer.metadata.userId) { await stripe.customers.update(customer.id, { metadata: { userId } }) } } #LINEDC #JP_Stripes
  16. CheckoutもPayment Linksもまとめて処理する場合 • どちらもCheckoutの Webhookイベントを利用する • Payment LinksのIDがあるかないかで 分岐が可能 •

    LINEのUser IDを取得する場所の差を 吸収する ◦ Plinks: data.client_reference_id ◦ Checkout: data.metadata(など) 16 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 const data = event.data.object as any if ( ([ 'checkout.session.completed', 'checkout.session.async_payment_succeeded' ].includes(event.type)) ) { // Payment Links if (data.payment_link) { } else { // Checkout Sessions } } #LINEDC #JP_Stripes
  17. Agenda • Search APIを使わずにLINEのユーザーとStripeのCustomerを連携 • Stripe Search API入門 • Search

    APIを使った顧客情報連携方法 ◦ Checkout編 ◦ Payment Links編 • Search APIを顧客管理やプロモーションに活用する 17 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes
  18. 特定の顧客にLINEでプロモメッセージ • `-field:null`を応用して、 「LINE IDを持つ」顧客を検索 • PaymentIntentsに検索をかけて、 「先月X円以上注文した顧客」を 絞り込み •

    LINEのbot-sdkで、pushメッセージ ◦ Stripeのクーポンコードを送付 ◦ 特別価格のPayment Links URL ◦ Checkout Sessionを使った 限定セールURL ◦ etc… 18 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 await stripe.customers.search({ query: `-metadata['userId']:null` }) const {data: intents} = await stripe.paymentIntents.search({ query: [ `created>${targetMonth.startOf('month').unix()}`, 'AND', `created<${targetMonth.endOf('month').unix()}`, 'AND', `customer:"${customerId}"`, 'AND', `status:"succeeded"`, 'AND', 'currency:"JPY"', ].join(' '), }) #LINEDC #JP_Stripes
  19. 注文履歴を表示するLINE botの例 • Search APIで顧客と注文を特定 • CheckoutとInvoiceのデータを取得し、 注文詳細を取得 ◦ pi.invoiceで請求書かを判定

    ◦ Checkout APIに pi.idを渡して検索 ◦ レスポンスの型を揃えれば、 両方の履歴をまとめて出せる • LINEのbot-sdkで、replyメッセージ ◦ Flex Messageでリッチな表示に ◦ 再注文ボタンなどがあると、 より便利かも 19 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 if (req.method?.toLocaleLowerCase() === 'post') { await runMiddleware(req, res, middleware); await Promise.all(req.body.events.map(async event => { if (event.type === 'message') { if (event.message.type !== 'text') return; if (!/注文履歴/.test(event.message.text)) return; const userId = event.source.userId; const {data: customers} = await stripe.customers.search({ query: `metadata['userId']:"${userId}"`, limit: 100, }) const histories = await listCheckoutHistory(customers[0].id) const orderedProducts = await listOrderedProducts(histories) await Promise.all(histories.map(async (history) => { await sendOrderHistory(history, event.replyToken, orderedProducts) })) Full: https://zenn.dev/link/comments/b5319e4143c0ab
  20. [注文履歴]と入れると、注文履歴がでる 20 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes

  21. まとめ • Stripe Search APIで、LINEのUserとStripe Customerの連携が楽になる • 注文履歴や、顧客ごとの月間決済高の検索もより手軽に • 秒単位とはいえ、Rate

    Limitがある点には要注意 ◦ MVPではSearch APIを使った紐付けをしつつ、 どこかでKVS系のデータストアへの移行も要検討 • bot-sdk / Webhookと組み合わせて、よりリッチなチャットコマースを! 21 Stripe Search APIを利用した、LINEとStripeの顧客情報連携 #LINEDC #JP_Stripes