最近stripeでやったことを備忘録として適当にまとめる。
注意点
dictだったりクラスだったり、指定しているキーの間違いなど、一部ごちゃごちゃになっている部分があるかもなのでコピペ利用したい時は要確認。
sdk導入
pip install stripe
customer
作成
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
stripe_customer = stripe.Customer.create(name="hoge")
取得
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
customer = stripe.Customer.retrieve("stripe_customer_id")
削除
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
stripe.Customer.delete("stripe_customer_id")
card
一覧取得
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
cards = stripe.PaymentMethod.list(
customer="stripe_customer_id",
type="card",
)
一覧取得(ページネーション)
stripeのページネーションの仕組みははカードに限らず他の一覧取得でも使う仕様。
一覧取得レスポンスにて、次ページがあれば has_more == True
となり、starting_afterにリストの末尾のオブジェクトidを指定して再度リストをコールする。
末尾のオブジェクトidというのは以下の例でいうlatest_id。
"""
例
"""
cards = stripe.PaymentMethod.list()
has_more = cards.has_more
latest_id = cards.data[-1].id
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
cards = stripe.PaymentMethod.list(
customer="stripe_customer_id",
type="card",
starting_after="latest_id",
)
デフォルト支払いカード変更
オブジェクトのidに応じて処理分け。
コンソールからや旧APIを使った場合は card_xxxxx
というidでカードが作られ、payment intent apiを使った場合は pm_xxxxx
というidでカードが作られる。
default_sourceは変更はできるが空にはできないはず。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
# リクエストボディ等から受け取った値を判定し、オブジェクトのidに応じて処理分け
card_id = "card_xxxxx"
if card_id.startswith("card_"):
stripe.Customer.modify(
"stripe_customer_id",
default_source=card_id,
invoice_settings={"default_payment_method": ""},
)
elif card_id.startswith("pm_"):
stripe.Customer.modify(
"stripe_customer_id",
invoice_settings={"default_payment_method": card_id},
)
カード削除(紐付け解除)
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
stripe.PaymentMethod.detach("card_id")
setup intent
作成
setup intentはpayment intentとほぼ同じだが、setup intentは決済が行われない。
カードの登録だけしたい、みたいな時に使う。
returnしている値はフロントでフォームを描画する際に使う。
そこでのカード情報などの入力値をstripe側と通信して保存する。
awsのs3におけるpresignedUrlに近いイメージ。
バックエンドはurlやsecretを発行し、あとはフロント <-> サービスで連携させる感じ。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
setup_intent = stripe.SetupIntent.create(
customer="stripe_customer_id",
)
return {
"setup_intent_id": setup_intent.id,
"client_secret": setup_intent.client_secret
}
payment intent
作成
日本円で1000円のpayment intent作成の例。
旧chargeのような感じ。
指定のカードで決済まで行う。
特にパラメータを指定していない最低限の状態であれば、レスポンスのclient_secret等を用いてフロント側でフォームを描画、カード情報を入力させてstripeと通信/決済。
しかし以下ケースの場合はすでにカードが登録されてる前提のもとpayment intent作成時に決済までされるようにしている例。
ついでにメタデータも使ってる例。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
# サンプル数値
amount = 1000
pi = stripe.PaymentIntent.create(
customer="stripe_customer_id",
amount=amount,
currency="jpy",
payment_method="card_id",
confirm=True,
automatic_payment_methods={"enabled": True, "allow_redirects": "never"},
metadata={"project_id": "project_id"},
)
検索
特定の期間を指定したpayment intentの検索。
from datetime import datetime
import calendar
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
stripe_customer_id = "xxxxx"
amount = 1000
now = datetime.utcnow()
month_end = calendar.monthrange(now.year, now.month)[1]
start = int(datetime(now.year, now.month, 1).timestamp())
end = int(datetime(now.year, now.month, month_end, 23, 59, 59).timestamp())
result = stripe.PaymentIntent.search(
query=f"status:'succeeded' AND customer:'{stripe_customer_id}' AND amount:{amount} AND created>={start} AND created<={end}",
)
subscription
作成
商品(product)、価格(price)をコンソールで事前に作成する。
price_idをフロントなり環境変数なりベタ書きなり、どこかから受け取って処理したりする。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
subscription = stripe.Subscription.create(
customer="stripe_customer_id",
items=[
{
"price": "price_id",
}
],
default_payment_method="card_id",
)
一覧取得
ページネーションの場合は starting_after
を使うようにしている。
subscriptions.dataは、サブスクリプション(dict)の配列。
expand
にてレスポンスの拡張をしている。
subscriptions.data[0].default_payment_methodは通常payment method idあたりがstringで入ってたはずだが、expandで拡張をすると、payment method idを指定して取得できるpaiment method オブジェクトがincludeされた状態になる。
以下のイメージ
-- 拡張してないやつ
SELECT * FROM subscriptions;
-- 拡張したやつ
SELECT * FROM subscriptions
LEFT JOIN payment_methods ON subscriptions.payment_method_id = payment_methods.id;
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
# クエリパラメータ等で受け取る
latest_id = "xxxxx"
if latest_id:
subscriptions = stripe.Subscription.list(
customer="stripe_customer_id",
expand=["data.default_payment_method"],
starting_after=latest_id,
)
else:
subscriptions = stripe.Subscription.list(
customer="stripe_customer_id",
expand=["data.default_payment_method"],
)
取得
一覧では expand=["data.default_payment_method"]
となっていたが、単一の場合はレスポンスの構造が異なるのでそれに合わせて expand=["default_payment_method"]
となっている。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
subscription = stripe.Subscription.retrieve(
"subscription_id",
expand=["default_payment_method"],
)
更新
サブスクリプションに紐づく支払いカードを変更する
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
# リクエストボディなどで受け取る
card_id = "xxxxx"
subscription = stripe.Subscription.modify(
"subscription_id",
default_payment_method=card_id
)
参考
default_payment_method
以上に柔軟にいろいろできる
トライアル終了時またはサブスクリプション期間終了時に自動でサブスクリプションがキャンセルされるよう設定する
サブスク期間にキャンセルを行っても期間の終了までは利用できるようにしたい、といった場合に使う。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
stripe.Subscription.modify(
"stripe_subscription_id",
cancel_at_period_end=True,
)
参考
任意に指定したタイミングでキャンセルしたいなら cancel_at
を使う
https://stripe.com/docs/api/subscriptions/update#update_subscription-cancel_at
invoice
検索
特定の条件で絞り込みを行いたかったため、 .list()
ではなく .search()
を使っている。
以下ではトライアル設定を行った際に発行される0円のinvoiceを除外した一覧を取得。
search apiでのページネーションは page
パラメータを使う。
レスポンスの next_page
を page
に指定する。
"""
例
"""
invoices = stripe.Invoice.search()
next_page = invoices.next_page
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
# クエリパラメータ等で受け取った値
next_page = "xxxxx"
stripe_customer_id = "xxxxx"
# サブスクの更新(トライアル設定)によって作成される0円のinvoiceを除外
if next_page:
invoices = stripe.Invoice.search(
query=f"customer:'{stripe_customer_id}' AND -total:0",
page=next_page,
)
else:
invoices = stripe.Invoice.search(
query=f"customer:'{stripe_customer_id}' AND -total:0",
)
取得
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
invoice = stripe.Invoice.retrieve("invoice_id")
webhook
必要に応じて設定する。
コンソール > 開発者 あたりから設定できる。
デフォルトカードを設定する
payment_method.attached
イベントが発生した場合にコールされるapiを用意しており、カード登録時にデフォルトがなければ設定する。
import stripe
stripe.api_key = "STRIPE_SECRET_KEY"
pm_id = body.data.object.id
customer_id = body.data.object.customer
customer = stripe.Customer.retrieve(customer_id)
has_default_pm = False
if customer.invoice_settings.default_payment_method:
has_default_pm = True
if customer.default_source:
has_default_pm = True
if not has_default_pm:
if pm_id.startswith("card_"):
stripe.Customer.modify(
customer_id,
default_source=pm_id,
invoice_settings={"default_payment_method": ""},
)
elif pm_id.startswith("pm_"):
stripe.Customer.modify(
customer_id,
invoice_settings={"default_payment_method": pm_id},
)
トライアル終了日を設定する
invoice.paid
イベントが発生した場合にコールされるapiを用意しており、サブスクリプションに関連する支払いの場合にトライアルを設定している。
初回は当月 + 1年を期間とし、次回以降は年次で請求サイクルを回したかった。
stripeに質問したところ、初回決済完了後にトライアルを設定し、トライアル終了日を初回のサブスク期限にすることで実現可能と助言いただいた。
パラメータの指定で比例配分の無効化
= 日割計算はしない
= 有効なサブスクリプションに対する請求サイクルの変更時に、請求額の調整を行わない
= 次回請求から勝手に日割りで割引される等がなくなる
# ---webhookのリクエストから、サブスクであるか/初回支払い完了時かなどを確認するためのフィルタリング処理など---
# トライアル終了日計算
now = datetime.utcnow()
month_end = calendar.monthrange(now.year, now.month)[1]
next_year_month_end = datetime(now.year + 1, now.month, month_end)
next_year_month_end_unixtime = int(next_year_month_end.timestamp())
# サブスク更新
subscription = stripe.Subscription.modify(
subscription_id,
trial_end=trial_end,
proration_behavior="none",
)
オートページネーション(2023/11/16追記)
自動でページネーションする仕組みがあると教えてもらった