Checkout Session で割引を動的に追加または削除する方法をご紹介します。
ご利用事例
このガイドでは、社内の割引システムを導入して動的な金額割引を作成する方法を説明します。合わせて、次についても理解できます。
- ロイヤルティ割引の適用: 顧客ロイヤルティ段階または購入履歴に基づき、割引を自動的に適用します。
- カート金額のプロモーション: 注文合計が特定の基準値を超えた場合に割引を追加します (たとえば、100 ドルを超える注文から 10 ドル割引など)。
- 時間的制約のあるオファー: 期間限定のプロモーション割引を適用したり、期限切れの割引コードを削除したりできます。
- ロケーションベースの割引: 顧客の配送先住所に基づいて、地域別に割引を適用します。
- 顧客固有のオファー: 顧客セグメントまたは以前の購買行動に基づき、個人に合わせた割引を作成します。
Stripe の公式ライブラリを使用して、アプリケーションから Stripe API にアクセスします。
gem install stripe -v 15.1.0-beta.2
このプレビュー機能を使用するには、まず SDK を更新して、checkout_server_update_beta=v1
ベータ版ヘッダーを使用します。
Stripe.api_key = 'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
Stripe.api_version = '2025-03-31.basil; checkout_server_update_beta=v1;'
決済セッションの作成時に permissions.update_discounts=server_only を渡し、サーバーからの割引の更新を有効にします。このオプションを渡すと、クライアント側の割引申し込みが無効になり、すべての割引ロジックがサーバーを通過するようになります。
curl https://api.stripe.com/v1/checkout/sessions \
-u "sk_test_BQokikJOvBiI2HlWgH4olfQ2
:" \
-H "Stripe-Version: 2025-03-31.basil; checkout_server_update_beta=v1;" \
-d ui_mode=custom \
-d "permissions[update_discounts]"=server_only \
-d "line_items[0][price]"= \
-d "line_items[0][quantity]"=1 \
-d mode=payment \
--data-urlencode return_url="https://example.com/return"
サーバーで新しいエンドポイントを作成し、決済セッションに割引を適用します。これは、後のステップでフロントエンドから呼び出します。
セキュリティのヒント
クライアント側のコードは、ユーザーによって完全に制御された環境で実行されます。悪意のあるユーザーは、クライアント側の検証を迂回し、リクエストを代行受信して変更したり、サーバーへのまったく新しいリクエストを作成したりすることができます。
エンドポイントを設計する際は、以下を考慮することをお勧めします。
- 特定の顧客とのやり取りを過度に汎用的ではないように (一般的な「更新」アクションではなく「ロイヤルティ割引の適用」など)、エンドポイントを設計します。特定のエンドポイントは、目的を明確に保ち、検証ロジックの記述と保守を容易にするのに役立ちます。
- クライアントからエンドポイントにセッションデータを直接渡さないでください。悪意のあるクライアントがリクエストデータを変更し、決済セッションの状態を判別するに当たり信頼できないソースにする可能性があります。代わりに、セッション ID をサーバーに渡し、Stripe の API からデータを安全に取得してください。
require 'sinatra'
require 'json'
require 'stripe'
set :port, 4242
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
def validate_discounts(discounts, session
return true if discounts.empty? || discounts == ""
return false if discounts.is_a?(Array) && discounts.length > 1
true
end
def recompute_discounts(discounts, session)
return [] if discounts.empty? || discounts == ""
customer_id = session.customer || session.client_reference_id
cart_total = session.amount_total
discount_amount = calculate_customer_discount(customer_id, cart_total)
if discount_amount > 0
[{
coupon_data: {
name: "Customer Discount",
amount_off: discount_amount,
currency: session.currency || 'usd'
}
}]
else
[]
end
end
def calculate_customer_discount(customer_id, cart_total)
if cart_total > 10000
discount = [cart_total * 0.1, 2000].min
discount.to_i
else
0
end
end
post '/update-discounts' do
content_type :json
request.body.rewind
request_data = JSON.parse(request.body.read)
checkout_session_id = request_data['checkout_session_id']
discounts = request_data['discounts']
session = Stripe::Checkout::Session.retrieve(checkout_session_id)
if !validate_discounts(discounts, session)
return { type: 'error', message: 'Your discounts are invalid. Please refresh your session.' }.to_json
end
discounts = recompute_discounts(discounts, session)
if discounts
Stripe::Checkout::Session.update(checkout_session_id, {
discounts: discounts,
})
return { type: 'object', value: { succeeded: true } }.to_json
else
return { type: 'error', message: "We could not update your discounts. Please try again." }.to_json
end
end
custom_checkout_server_updates_1
ベータヘッダーを使用して stripe.js を初期化します。
const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx'
, {
betas: ['custom_checkout_server_updates_1'],
});
フロントエンドから更新をトリガーするには、サーバーにリクエストを行い、runServerUpdateでラップします。リクエストが成功すると、セッション オブジェクトが新しい割引で更新されます。
<button id="apply-customer-discount">
Apply Customer Discount
</button>
document.getElementById('apply-customer-discount')
.addEventListener("click", async (event) => {
const updateCheckout = () => {
return fetch("/apply-customer-discount", {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify({
checkout_session_id: checkout.session().id,
})
});
};
const response = await checkout.runServerUpdate(updateCheckout);
if (!response.ok) {
return;
}
event.target.textContent = "Discount Applied!";
event.target.disabled = true;
});
導入内容をテストして、動的な割引が正しく機能することを確認するには、以下のステップに従います。
本番環境を反映したサンドボックス環境を設定します。この環境では、Stripe サンドボックスの API キーを使用してください。
さまざまな割引シナリオをシミュレーションして、recomputeDiscounts
関数がさまざまなシナリオを正しく処理することを確認できます。
ログツールやデバッグツールを使用してサーバーが以下を行っていることを確認し、サーバー側のロジックを検証します。
ブラウザーで決済プロセスを複数回実行し、クライアント側のロジックを検証します。割引の適用後に UI がどのように更新されるかに注意してください。以下を確認してください。
- runServerUpdate 関数が想定どおりに呼び出される。
- 割引は事業のロジックに基づき正しく適用されます。
- Checkout の合計が更新され、適用される割引が反映されます。
- 割引の申し込みが失敗した場合は、エラーメッセージが正しく表示されます。
無効な割引リクエストなど、さまざまな割引シナリオをテストするか、サーバーエラーをシミュレーションして、サーバー側とクライアント側の両方でエラー処理をテストします。