🎁

DBの無い世界線でECアプリを作ってみた話。〜Stripe Search APIを活用〜

2022/05/11に公開約5,800字

作ったもの

自社でDBを持たず、Stripeのダッシュボードで顧客管理、商品管理、注文管理ができる、LINEチャットコマースアプリを作りました。

友だち追加から商品詳細

単品商品購入手順

定期販売購入手順

定期販売のキャンセル手順

作った経緯

2022年4月に公開されたStripe Search APIにより、Stripeに保存している顧客、注文、商品、といった情報にクエリをかけることができるようになりました。

https://stripe.com/docs/search

それにより、「自社でDBを作らなくても、Stripeに保存した情報だけでアプリが作れるのでは?」と思い、一番作り慣れているLINE×ECアプリで実験することにしました。

しかしStripe APIだけでは、以下の処理に対応できません。

  • 在庫管理
  • トランザクション
  • 注文のステータス管理(商品準備中や配送中など)

ECサイトにおいて在庫管理は生命線といれるかもしれません。
しかし、SUZURIのようなオーダーメイドサービスを見て「注文されてから商品を作るビジネスもあるんだ」と思い在庫管理機能をなくしたECアプリを作ってみました。
他にもアパレルで多い予約販売をベースにしたECアプリにも活用でできるかと思います。

使用したStripe API

使用したStripe APIは以下の8つです。

  • Search API
  • Customer
  • Checkout Session
  • Product
  • Prices
  • Invoice
  • Billing Portal
  • Webhook(APIではないけど)

友だち追加時に使用したAPI

CustomerとSearch APIを活用してます。

const { data: customers } = await stripe.customers.search({ query: `metadata['userId']:'${userId}'` })
if (customers.length === 0) {
  const lineProfile = await lineClient.getProfile(userId)
  return await stripe.customers.create({
    name: lineProfile.displayName || '未設定',
    description: userId,
    metadata: {
      userId
    }
  })
} else {
  return data[0]
}

customer作成時にmetadata内にuserId属性を作成することで、searchする際に以下のクエリを書くことで検索できます。

await stripe.customers.search({ query: `metadata['userId']:'${userId}'` })

参考

https://stripe.com/docs/api/customers/search

商品一覧と商品詳細に使用したAPI

ProductとPriceを使用してます。

export interface MsgProductList {
  productId: string
  priceId: string
  name: string
  imgUrl: string
  amount: number
}

const getProducts = async (): Promise<Stripe.Product[]> => {
  const { data } = await stripe.products.list()
  return data
}

const products = await getProducts()
const _products: MsgProductList[] = []
await Promise.all(
  products.map(async (product) => {
    // @ts-ignore
    const price = await stripe.prices.retrieve(product.default_price)
    _products.push({
      productId: product.id,
      priceId: price.id,
      name: product.name,
      imgUrl: product.images[0],
      amount: Number(price.unit_amount)
    })
  })
)

MsgProductListは自分がLINE Flexメッセージに必要な情報をまとめた型になるため各々でよしなに変更をお願いします。
product内には、default_priceという値が型に存在しなかったため、@ts-ignoreをさせていただきました。(今後改善されるかも)

商品管理は、Stripeダッシュボードの「商品」より可能です。

定期購入で使用したAPI

定期購入では、checkout.sessions.createを使用しました。

const { url } = await stripe.checkout.sessions.create({
  customer: customerId,
  line_items: [
    {
      price: priceId,
      quantity: 1
    }
  ],
  shipping_address_collection: {
    allowed_countries: ['JP']
  },
  mode: 'subscription',
  payment_method_types: ['card'],
  success_url: LINE_FRIEND_URL,
  cancel_url: LINE_FRIEND_URL
})
  • shipping_address_collectionallowed_countriesJPのみにすることで、配送先の入力が日本で固定されます。
  • modesubscriptionにする。
  • success_urlcancel_urlをLINE公式アカウント友だち追加URLにすることで、決済終了後に自動的にLINE公式アカウントのトーク画面に遷移します。

定期購入された注文は、Stripeダッシュボードの「支払い」→「サブスクリプション」より確認できます。

定期購入がキャンセルされた注文は、Stripeダッシュボードの「支払い」→「サブスクリプション」→「キャンセル済み」より確認できます。

単品購入で使用したAPI

単品購入には、invoiceを使用しました。
定期購入のようにcheckout.sessions.createを使用しなった理由は、こちらに記載してます。

await stripe.invoiceItems.create({ customer: customerId, price: priceId })
const { id: invoiceId } = await stripe.invoices.create({
  customer: customerId,
  payment_settings: { payment_method_types: ['card', 'konbini'] },
  collection_method: 'send_invoice',
  days_until_due: 7
})
await stripe.invoices.sendInvoice(invoiceId)
const { hosted_invoice_url } = await stripe.invoices.retrieve(invoiceId)
  • payment_method_typeskonbiniを追加することで、コンビニ決済が可能となります。
  • days_until_dueで支払いまでの有効日数を指定してます。

単品購入商品は、Stripeダッシュボードの「支払い」で確認できます。
しかし、この画面には定期販売の情報も反映されるため、説明列を「Payment for Invoice」でフィルターすればより見やすくなります。

注文履歴&マイページに使用したAPI

注文履歴&マイページには、billingPortal.sessions.createを使用しました。

const { url } = await stripe.billingPortal.sessions.create({
  customer: customer.id,
  return_url: LINE_FRIEND_URL
})
  • return_urlには、LINE公式アカウントの友だち追加URLを指定することで、Billing Portal画面から容易にLINE公式アカウントに遷移することができます。

工夫した点

注文履歴

マイページには、Billing Portalを活用しており、注文履歴や定期契約内容、個人情報はすべて確認&変更ができます。
注文履歴には、Billing Portal内の「インボイス履歴」を代替としてます。

しかし、ここに表示される情報はStripe Invoiceが発行された注文のみとなります。
通常のECには定期購入だけでなく単品購入もあり、Stripe Checkoutで定期販売の商品を購入すると自動的にInvoiceが作成されますが、単品商品では作成されません。
そのため、単品購入をCheckoutで処理するのではなくInvoiceで処理する必要があります。

クロスセル

Stripeダッシュボードで商品には、クロスセルという機能があります。
Checkout Sessionにて商品を購入する際、オススメしたい商品を設定することができるので購買率を上げる施策が可能です。

  • Stripeダッシュボード画面

  • Checkout Session画面

まとめ

Stripe Search APIによって、最小限のコストでECアプリが作れました。
在庫管理を妥協しなければならないデメリットはありますが、在庫を持たないビジネスには活用できる開発方法です。
今回紹介したSearch API以外にも活用できそうなものがあるので、興味ある方はぜひ使用してみてください。

https://stripe.com/docs/search

今後もStripeのアップデートが楽しみです!

Discussion

ログインするとコメントできます