🖥

#Stripe API / サブスクリプションのスケジュール登録が開始済みでも予約中でも SubscriptionSchedule を Up

2020/01/03に公開

points

  • 1ヶ月サイクルのプラン1個だけをスケジュール登録して、あとから end_date = キャンセル日を登録する場合
  • Subscription に対してであれば Update で cance_at を登録すれば良いのだが、SubscriptionSchedule が開始していない場合は、そもそも Subscription が発生していないため、できない
  • どちらの場合も SubscriptionSchedule への update で APIリクエストを完結させたいが、SubscriptionSchedule はちょっと癖があるやつ
  • SubscriptionSchedule が active = 開始している場合は、 current_phase.start_date を起点に、そこから数ヶ月後の end_date をぴったりと指定する。なぜなら中途半端な日時を指定すると、日割り計算的な請求が発生してしまうから。
  • SubscriptionSchedule が not_started= 開始していない場合は、 未来に開始される最初のフェーズ = phasse の start_date を起点に、そこから数ヶ月後の end_date をぴったりと指定する。なぜなら中途半端な日時を指定すると、日割り計算的な請求が発生してしまうから。ちなみに現在フェーズの期間 ( current_phase ) はサブスクリプションの請求サイクルとは異なる。
  • SubscriptionSchedule Update で phase 単位で iterations を指定することもできるだろうが、start_date / end_date を明確に与えてあげたほうが、場合によってはやりやすそうだ。いや、やりやすい方でお願いします。

Docs

参考 - Stripeの基本

日本正式リリースしたStripeを使ってサブスクリプション型決済システムを実装する - Qiita
https://qiita.com/tady/items/7617e62b2a5402ebd0fb

Stripe Billing 101 - Qiita
https://qiita.com/y_toku/items/235b5e7ee00792edcbbf

Stripe初心者のための基本的な使い方(Rails編) - Qiita
https://qiita.com/ryouzi/items/6ee8f277471aa3b02f7b

Docs

Code

# Docs

# https://stripe.com/docs/billing/subscriptions/subscription-schedules
# https://stripe.com/docs/billing/subscriptions/subscription-schedules/use-cases
#
# https://stripe.com/docs/api/subscription_schedules/release
# https://support.stripe.com/questions/create-update-and-schedule-subscriptions

require 'active_support/core_ext'
require 'stripe'

Stripe::api_key = ENV['STRIPE_SECRET_KEY']

[1.month, 3.months].each do |end_at_from|
  product1 = Stripe::Product.create(name: "Gold plan #{rand(9999999999)}")
  plan1 = Stripe::Plan.create(interval: 'month', currency: 'jpy', amount: 5000, product: product1.id, usage_type: 'licensed')

  tax_rate = Stripe::TaxRate.create(display_name: 'Tax Rate', percentage: 10.0, inclusive: false)
  customer = Stripe::Customer.create
  payment_method = Stripe::PaymentMethod.create(type: 'card', card: { number: '4242424242424242', exp_year: 2030, exp_month: 01})
  customer_payment_method = Stripe::PaymentMethod.attach(payment_method.id, customer: customer.id)

  def put_subscription_schedule(subscription_schedule, message)
    puts '-' * 100
    puts "Subscription Schedule"
    puts message
    puts '-' * 100
    puts subscription_schedule
    puts '-' * 100
    puts "https://dashboard.stripe.com/test/subscription_schedules/#{subscription_schedule.id}"
  end

  # https://stripe.com/docs/api/subscription_schedules/create
  soon_start_subscription_schedule = Stripe::SubscriptionSchedule.create(
    {
      customer: customer.id,
      start_date: Time.now.to_i + 5,
      default_settings: {
        default_payment_method: customer_payment_method.id,
      },
      phases: [
        {
          plans:
            [
              { plan: plan1.id, quantity: 1 },
            ],
          prorate: false,
          default_tax_rates: [tax_rate],
        },
      ],
    }
  )

  puts '-' * 100
  puts "Wait until subscription schedule starts"
  puts '-' * 100
  until soon_start_subscription_schedule.status == 'active' do
    soon_start_subscription_schedule = Stripe::SubscriptionSchedule.retrieve(soon_start_subscription_schedule.id)
    puts soon_start_subscription_schedule.status
    sleep 2
  end

  started_subscription_schedule = Stripe::SubscriptionSchedule.retrieve(id: soon_start_subscription_schedule.id, expand: ['subscription'])

  future_subscription_schedule = Stripe::SubscriptionSchedule.create(
    {
      customer: customer.id,
      start_date: Time.now.to_i + 100*24*60*60,
      default_settings: {
        default_payment_method: customer_payment_method.id,
      },
      phases: [
        {
          plans:
            [
              { plan: plan1.id, quantity: 1 },
            ],
          prorate: false,
          default_tax_rates: [tax_rate],
        },
      ],
    }
  )

  # current_phase.start_date is not subscription cycle start_date
  def update_subscription_schedule(base_subscription_schedule)
    if base_subscription_schedule.status == 'active'
      start_date = base_subscription_schedule.current_phase.start_date
    elsif base_subscription_schedule.status == 'not_started'
      start_date = base_subscription_schedule.phases[0].start_date
    else
      raise
    end

    end_date = Time.at(start_date).since(3.months).to_i

    Stripe::SubscriptionSchedule.update(
      base_subscription_schedule.id,
      {
        # Set "cancel" not "releasse"
        end_behavior: 'cancel',
        phases: [
          {
            plans:
              [
                {
                  plan:     base_subscription_schedule.phases[0].plans[0].plan,
                  quantity: base_subscription_schedule.phases[0].plans[0].quantity
                },
              ],
            # prorate does not effect to Subscription or SubscriptionSchedule Canceling
            # prorate: true / false,
            default_tax_rates: base_subscription_schedule.phases[0].default_tax_rates.map(&:id),
            # For Update You must set start_date in first phase not in subscription schedule directly
            start_date: start_date,
            # If you do not want to get upcoming invoice on Subscription
            # Then you must specify end_date with Subscription natural cycle interval
            end_date: end_date
          },
        ]
      }
    )
  end

  updated_started_subscription_schedule = update_subscription_schedule(started_subscription_schedule)
  updated_future_subscription_schedule = update_subscription_schedule(future_subscription_schedule)

  put_subscription_schedule(updated_started_subscription_schedule, 'UPDATED')
  put_subscription_schedule(updated_future_subscription_schedule, 'UPDATED')
end

# NOTE
# SubscriptionSchedule current_phase is not same as Subscription cycle
# it is "phase" start_date and end_date
#
# If phase end_date is 3.months after then ...
#
# Time.at(updated_started_subscription_schedule.current_phase.start_date)
# => 2020-01-02 17:44:23 +0900
# Time.at(updated_started_subscription_schedule.current_phase.end_date)
# => 2020-04-02 17:44:23 +0900

Started Subscription - cancel at current phase

Upcoming invoice leaves from dashboard

image

Not Started Subscription - cancel at first phase

Theres Starts Ends in both in future
Theres upcoming invoice becauce Schedule will start in future
Not current invoice because Schedule not started

image

Started Subscription - cancel at future phase

Change end_date in code and run Case

image

Not Started Subscription - cancel at not first phase

Change end_date in code and run Case

image

Result

  • This is run code resutls example but not correctly same as Image capture images in this article.
----------------------------------------------------------------------------------------------------
Wait until subscription schedule starts
----------------------------------------------------------------------------------------------------
not_started
not_started
not_started
not_started
not_started
not_started
active
----------------------------------------------------------------------------------------------------
Subscription Schedule
CREATED
----------------------------------------------------------------------------------------------------
{
  "id": "sub_sched_1FwPNZCmti5jpytUe3xhuCdN",
  "object": "subscription_schedule",
  "canceled_at": null,
  "completed_at": null,
  "created": 1577955005,
  "current_phase": null,
  "customer": "cus_GTM5yQ0vRozYUW",
  "default_settings": {
    "billing_thresholds": null,
    "collection_method": "charge_automatically",
    "default_payment_method": "pm_1FwPNHCmti5jpytURUDXSKtE",
    "default_source": null,
    "invoice_settings": null
  },
  "end_behavior": "release",
  "livemode": false,
  "metadata": {
  },
  "phases": [
    {
      "application_fee_percent": null,
      "billing_thresholds": null,
      "collection_method": null,
      "coupon": null,
      "default_payment_method": null,
      "default_tax_rates": [
        {
          "id": "txr_1FwPNHCmti5jpytUA4yh9gmW",
          "object": "tax_rate",
          "active": true,
          "created": 1577954987,
          "description": null,
          "display_name": "Tax Rate",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 10.0
        }
      ],
      "end_date": 1589187005,
      "invoice_settings": null,
      "plans": [
        {
          "billing_thresholds": null,
          "plan": "plan_GTM5XKzi7c1qy8",
          "quantity": 1,
          "tax_rates": [

          ]
        }
      ],
      "prorate": false,
      "start_date": 1586595005,
      "tax_percent": 10.0,
      "trial_end": null
    }
  ],
  "released_at": null,
  "released_subscription": null,
  "renewal_interval": null,
  "revision": "sub_sched_rev_1FwPNZCmti5jpytUWUBqK4lG",
  "status": "not_started",
  "subscription": null
}
----------------------------------------------------------------------------------------------------
https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1FwPNZCmti5jpytUe3xhuCdN
----------------------------------------------------------------------------------------------------
Subscription Schedule
UPDATED
----------------------------------------------------------------------------------------------------
{
  "id": "sub_sched_1FwPNJCmti5jpytU8hFkMssH",
  "object": "subscription_schedule",
  "canceled_at": null,
  "completed_at": null,
  "created": 1577954989,
  "current_phase": {
    "end_date": 1585817393,
    "start_date": 1577954993
  },
  "customer": "cus_GTM5yQ0vRozYUW",
  "default_settings": {
    "billing_thresholds": null,
    "collection_method": "charge_automatically",
    "default_payment_method": "pm_1FwPNHCmti5jpytURUDXSKtE",
    "default_source": null,
    "invoice_settings": {
      "days_until_due": null
    }
  },
  "end_behavior": "cancel",
  "livemode": false,
  "metadata": {
  },
  "phases": [
    {
      "application_fee_percent": null,
      "billing_thresholds": null,
      "collection_method": null,
      "coupon": null,
      "default_payment_method": null,
      "default_tax_rates": [
        {
          "id": "txr_1FwPNHCmti5jpytUA4yh9gmW",
          "object": "tax_rate",
          "active": true,
          "created": 1577954987,
          "description": null,
          "display_name": "Tax Rate",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 10.0
        }
      ],
      "end_date": 1585817393,
      "invoice_settings": null,
      "plans": [
        {
          "billing_thresholds": null,
          "plan": "plan_GTM5XKzi7c1qy8",
          "quantity": 1,
          "tax_rates": [

          ]
        }
      ],
      "prorate": true,
      "start_date": 1577954993,
      "tax_percent": 10.0,
      "trial_end": null
    }
  ],
  "released_at": null,
  "released_subscription": null,
  "renewal_interval": null,
  "revision": "sub_sched_rev_1FwPNZCmti5jpytUoCQuoPaf",
  "status": "active",
  "subscription": "sub_GTM59xT4BS5E3j"
}
----------------------------------------------------------------------------------------------------
https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1FwPNJCmti5jpytU8hFkMssH
----------------------------------------------------------------------------------------------------
Subscription Schedule
UPDATED
----------------------------------------------------------------------------------------------------
{
  "id": "sub_sched_1FwPNZCmti5jpytUe3xhuCdN",
  "object": "subscription_schedule",
  "canceled_at": null,
  "completed_at": null,
  "created": 1577955005,
  "current_phase": null,
  "customer": "cus_GTM5yQ0vRozYUW",
  "default_settings": {
    "billing_thresholds": null,
    "collection_method": "charge_automatically",
    "default_payment_method": "pm_1FwPNHCmti5jpytURUDXSKtE",
    "default_source": null,
    "invoice_settings": {
      "days_until_due": null
    }
  },
  "end_behavior": "cancel",
  "livemode": false,
  "metadata": {
  },
  "phases": [
    {
      "application_fee_percent": null,
      "billing_thresholds": null,
      "collection_method": null,
      "coupon": null,
      "default_payment_method": null,
      "default_tax_rates": [
        {
          "id": "txr_1FwPNHCmti5jpytUA4yh9gmW",
          "object": "tax_rate",
          "active": true,
          "created": 1577954987,
          "description": null,
          "display_name": "Tax Rate",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 10.0
        }
      ],
      "end_date": 1594457405,
      "invoice_settings": null,
      "plans": [
        {
          "billing_thresholds": null,
          "plan": "plan_GTM5XKzi7c1qy8",
          "quantity": 1,
          "tax_rates": [

          ]
        }
      ],
      "prorate": true,
      "start_date": 1586595005,
      "tax_percent": 10.0,
      "trial_end": null
    }
  ],
  "released_at": null,
  "released_subscription": null,
  "renewal_interval": null,
  "revision": "sub_sched_rev_1FwPNaCmti5jpytUnlvLBTke",
  "status": "not_started",
  "subscription": null
}
----------------------------------------------------------------------------------------------------
https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1FwPNZCmti5jpytUe3xhuCdN
----------------------------------------------------------------------------------------------------
Wait until subscription schedule starts
----------------------------------------------------------------------------------------------------
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
not_started
active
----------------------------------------------------------------------------------------------------
Subscription Schedule
CREATED
----------------------------------------------------------------------------------------------------
{
  "id": "sub_sched_1FwPOZCmti5jpytUqLGjGd5a",
  "object": "subscription_schedule",
  "canceled_at": null,
  "completed_at": null,
  "created": 1577955067,
  "current_phase": null,
  "customer": "cus_GTM5Wlau0N9HHj",
  "default_settings": {
    "billing_thresholds": null,
    "collection_method": "charge_automatically",
    "default_payment_method": "pm_1FwPNcCmti5jpytUPG6hdjRk",
    "default_source": null,
    "invoice_settings": null
  },
  "end_behavior": "release",
  "livemode": false,
  "metadata": {
  },
  "phases": [
    {
      "application_fee_percent": null,
      "billing_thresholds": null,
      "collection_method": null,
      "coupon": null,
      "default_payment_method": null,
      "default_tax_rates": [
        {
          "id": "txr_1FwPNbCmti5jpytUOMYY1jCE",
          "object": "tax_rate",
          "active": true,
          "created": 1577955007,
          "description": null,
          "display_name": "Tax Rate",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 10.0
        }
      ],
      "end_date": 1589187067,
      "invoice_settings": null,
      "plans": [
        {
          "billing_thresholds": null,
          "plan": "plan_GTM5akDaPHCKOr",
          "quantity": 1,
          "tax_rates": [

          ]
        }
      ],
      "prorate": false,
      "start_date": 1586595067,
      "tax_percent": 10.0,
      "trial_end": null
    }
  ],
  "released_at": null,
  "released_subscription": null,
  "renewal_interval": null,
  "revision": "sub_sched_rev_1FwPOZCmti5jpytUsHM0JqYJ",
  "status": "not_started",
  "subscription": null
}
----------------------------------------------------------------------------------------------------
https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1FwPOZCmti5jpytUqLGjGd5a
----------------------------------------------------------------------------------------------------
Subscription Schedule
UPDATED
----------------------------------------------------------------------------------------------------
{
  "id": "sub_sched_1FwPNdCmti5jpytUnvii1fUR",
  "object": "subscription_schedule",
  "canceled_at": null,
  "completed_at": null,
  "created": 1577955009,
  "current_phase": {
    "end_date": 1585817414,
    "start_date": 1577955014
  },
  "customer": "cus_GTM5Wlau0N9HHj",
  "default_settings": {
    "billing_thresholds": null,
    "collection_method": "charge_automatically",
    "default_payment_method": "pm_1FwPNcCmti5jpytUPG6hdjRk",
    "default_source": null,
    "invoice_settings": {
      "days_until_due": null
    }
  },
  "end_behavior": "cancel",
  "livemode": false,
  "metadata": {
  },
  "phases": [
    {
      "application_fee_percent": null,
      "billing_thresholds": null,
      "collection_method": null,
      "coupon": null,
      "default_payment_method": null,
      "default_tax_rates": [
        {
          "id": "txr_1FwPNbCmti5jpytUOMYY1jCE",
          "object": "tax_rate",
          "active": true,
          "created": 1577955007,
          "description": null,
          "display_name": "Tax Rate",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 10.0
        }
      ],
      "end_date": 1585817414,
      "invoice_settings": null,
      "plans": [
        {
          "billing_thresholds": null,
          "plan": "plan_GTM5akDaPHCKOr",
          "quantity": 1,
          "tax_rates": [

          ]
        }
      ],
      "prorate": true,
      "start_date": 1577955014,
      "tax_percent": 10.0,
      "trial_end": null
    }
  ],
  "released_at": null,
  "released_subscription": null,
  "renewal_interval": null,
  "revision": "sub_sched_rev_1FwPOaCmti5jpytU2OqMiUUC",
  "status": "active",
  "subscription": "sub_GTM64npF4L1rop"
}
----------------------------------------------------------------------------------------------------
https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1FwPNdCmti5jpytUnvii1fUR
----------------------------------------------------------------------------------------------------
Subscription Schedule
UPDATED
----------------------------------------------------------------------------------------------------
{
  "id": "sub_sched_1FwPOZCmti5jpytUqLGjGd5a",
  "object": "subscription_schedule",
  "canceled_at": null,
  "completed_at": null,
  "created": 1577955067,
  "current_phase": null,
  "customer": "cus_GTM5Wlau0N9HHj",
  "default_settings": {
    "billing_thresholds": null,
    "collection_method": "charge_automatically",
    "default_payment_method": "pm_1FwPNcCmti5jpytUPG6hdjRk",
    "default_source": null,
    "invoice_settings": {
      "days_until_due": null
    }
  },
  "end_behavior": "cancel",
  "livemode": false,
  "metadata": {
  },
  "phases": [
    {
      "application_fee_percent": null,
      "billing_thresholds": null,
      "collection_method": null,
      "coupon": null,
      "default_payment_method": null,
      "default_tax_rates": [
        {
          "id": "txr_1FwPNbCmti5jpytUOMYY1jCE",
          "object": "tax_rate",
          "active": true,
          "created": 1577955007,
          "description": null,
          "display_name": "Tax Rate",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 10.0
        }
      ],
      "end_date": 1594457467,
      "invoice_settings": null,
      "plans": [
        {
          "billing_thresholds": null,
          "plan": "plan_GTM5akDaPHCKOr",
          "quantity": 1,
          "tax_rates": [

          ]
        }
      ],
      "prorate": true,
      "start_date": 1586595067,
      "tax_percent": 10.0,
      "trial_end": null
    }
  ],
  "released_at": null,
  "released_subscription": null,
  "renewal_interval": null,
  "revision": "sub_sched_rev_1FwPObCmti5jpytU5eKvVg9W",
  "status": "not_started",
  "subscription": null
}
----------------------------------------------------------------------------------------------------
https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1FwPOZCmti5jpytUqLGjGd5a


Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2915

チャットメンバー募集

何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ

Twitter

https://twitter.com/YumaInaura

公開日時

2020-01-03

Discussion