Oopsy! 一個頭兩個大的 Stripe 開發經驗

Stripe 是全球知名的線上金流服務,由於針對各種主流語言的支援度極高,加上清楚明瞭的開發者文件,使得 Stripe 也使許多知名公司,例如 Slack 以及 Booking.com 成為他們金流收費服務的使用者。遺憾的是,礙於法規緣故,目前在台灣仍然無法使用 Stripe 來處理境內交易。

在五倍紅寶石的期間,由於合作的公司使用 Stripe 作為他們的訂閱收費服務,因此筆者也有機會一探究竟 Stripe;然而,在 2019 年的 10 月,由於日本的消費稅進行全國性的調整,因此訂閱的收費也必須變更,卻也因此遇到了幾個麻煩,這篇文章將隱蔽合作公司的名稱及服務,闡述我們在更新訂閱稅率時遇到的一些問題。

也許是日本的服務經常使用 Stripe 來收費,針對稅率提升這個部分 Stripe 的官方文件有提出該如何調整消費稅率:https://support.stripe.com/questions/2019-10-1。簡單說一下作法:透過 API 在每個使用者的訂閱項目追加新的 Tax Object,並且利用 “upcoming invoice” 來確認是否有順利修改使用者在十月過後的消費稅。

由於更新稅率的時間點非常難以掌握,為了避免空窗期導致收取錯誤的消費稅額,因此我們執行兩次更新:第一次更新在九月三十以前,調整所有次回訂閱付款時間為十月以後的使用者稅率;而第二次則在十月一號當日進行所有使用者的調整。

倒上我們遇到了兩個狀況:取消訂閱的會員因為稅率提升而被重新訂閱,以及使用 Coupon 的會員遇到重複收費的情況。

明明取消了卻再次被收費?

十月的不久後我們便收到從客服的來信,有使用者表示他們明明已經取消了訂閱,卻又在十月的時候被收取了訂閱的費用。我們從後台清查,發現確實有數位使用者取消了訂閱之後,卻在更新稅率的時間點被重啟了訂閱。因此我們推測是更新稅率這個行為導致了預期外的結果。

正當我們清查程式碼以及 Stripe 後台時,我們發現了一件事情,Stripe 在 2018-02-28 的 API Patch Note 裡面提到:

Updating a subscription set to cancel on a future date no longer clears the cancellation status. In order to clear the cancellation status, specify `cancel_at_period_end=false` when updating a subscription.

這意味著原本只要更新訂閱的物件,將會恢復使用者的訂閱。但當下是 2019 年的十月,怎麼還會發生這件事情呢?接著我們便發現了,原來 Stripe 的 API 可以選擇不要升級版本!由於客戶的服務在早期就已經導入 Stripe,而開發其他功能的過程中也沒針對訂閱收費機制修改,因此我們並沒有發現 API 使用舊的版本。雖然說收費的功能一切正常,但很顯然參照最新的開發者文件來操作舊版的 API 是有風險的,即使我們一直都有關注 Client 端的版本升級,但卻遺漏了伺服器的 API 版本。

Coupon 失效?怎麼一回事?

Stripe 提供了折價功能,可以輕鬆地在使用者的訂閱上賦予各種折扣,客戶也因為商業需求提供免費訂閱一個月的 Coupon Code 給使用者。在清查被恢復訂閱的使用者的時候,我們發現有部分使用者的 Coupon 失效了,甚至在次月的訂閱被收取了比單月訂閱還要高的費用。

在調閱使用者的發票的時候,我們發現這些使用者的發票中都被註記了一條額外的費用:”Remaining time on [Plan Name] after 30 Sep 2019“;沿著這條線索我們找到了 Stripe 的 Proration 欄目:

Prorations come into play when a customer receives service for only part of the billing cycle. There are several cases where this can occur, notably when upgrading or downgrading a subscription during the billing cycle. Proration is only applicable to licensed (per-seat) subscriptions, as they are billed in advance–at the start of each cycle.

When a customer’s plan changes during a billing cycle, a proration line item is applied on their next invoice. If a customer whose subscription is billed on the first of the month changes to a more expensive plan, they receive a credit for the unused portion of the old plan and are billed for only the remainder of the month for the new plan.

Proration 主要會發生在當你的使用者有不同 Plan 選擇的時候,在訂閱期間倘若使用者更改成不同費用的 Plan,那麼 Stripe 將會依照比例來計算使用者必須額外付出的費用或者是可以得到的退費。

然而,由於 Stripe 針對 Proration 的文件說明有些曖昧不明,當時我們並不曉得更改稅率也會造成 Proration,事實上,更改稅率不應該被算在 upgrading or downgrading a subcription 裡頭,因此我們認為,由於使用者利用 Coupon 的關係,在 Stripe 計算過程當中,它將使用 coupon 的使用者判斷為免費的 Plan 切換到付費的 Plan,因此額外加算了 Proration 須付出的費用。

這樣的問題也相當容易解決,當你確信修改使用者訂閱時,你不會想要去計算使用者是否應該獲得退費或額外繳費,可以將 Proration 的功能關閉,便不會造成這樣的遺憾發生。

Prorating the costs of plans is the default behavior but you can disable proration by setting proration_behavior to none when making a subscription change:

雖然為了這次消費稅更新我們詳閱了許多官方文件和部落格資料,但很顯然並不是官方文件都能涵括所有的狀況,有些部分是開發者方的疏忽(例如利用舊版的 API 版本),而有些則是特殊的情況造成意外的結果。而這次更新也給我一個教訓,當開發者在整合外部服務與自己的應用程式時,應該更謹慎地對待兩者之間的契合度,並且隨時準備面對意外的發生以進行應對。

後記:事實上在發生 Proration 超收費用的時候,我社的負責人有寫信向日方的 Stripe 客服人員詢問 Proration 發生的原因,然而來回信件數次之後似乎都沒有得到有用的資訊,因此我們無法得知 Proration 真實的發生原因,因此上面提及的只能是說是最接近的推論。