server_payments_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. package server
  2. import (
  3. "encoding/json"
  4. "github.com/stretchr/testify/mock"
  5. "github.com/stretchr/testify/require"
  6. "github.com/stripe/stripe-go/v74"
  7. "heckel.io/ntfy/user"
  8. "heckel.io/ntfy/util"
  9. "io"
  10. "path/filepath"
  11. "strings"
  12. "testing"
  13. "time"
  14. )
  15. func TestPayments_Tiers(t *testing.T) {
  16. stripeMock := &testStripeAPI{}
  17. defer stripeMock.AssertExpectations(t)
  18. c := newTestConfigWithAuthFile(t)
  19. c.StripeSecretKey = "secret key"
  20. c.StripeWebhookKey = "webhook key"
  21. c.VisitorRequestLimitReplenish = 12 * time.Hour
  22. c.CacheDuration = 13 * time.Hour
  23. c.AttachmentFileSizeLimit = 111
  24. c.VisitorAttachmentTotalSizeLimit = 222
  25. c.AttachmentExpiryDuration = 123 * time.Second
  26. s := newTestServer(t, c)
  27. s.stripe = stripeMock
  28. // Define how the mock should react
  29. stripeMock.
  30. On("ListPrices", mock.Anything).
  31. Return([]*stripe.Price{
  32. {ID: "price_123", UnitAmount: 500},
  33. {ID: "price_456", UnitAmount: 1000},
  34. {ID: "price_999", UnitAmount: 9999},
  35. }, nil)
  36. // Create tiers
  37. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  38. ID: "ti_1",
  39. Code: "admin",
  40. Name: "Admin",
  41. }))
  42. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  43. ID: "ti_123",
  44. Code: "pro",
  45. Name: "Pro",
  46. MessagesLimit: 1000,
  47. MessagesExpiryDuration: time.Hour,
  48. EmailsLimit: 123,
  49. ReservationsLimit: 777,
  50. AttachmentFileSizeLimit: 999,
  51. AttachmentTotalSizeLimit: 888,
  52. AttachmentExpiryDuration: time.Minute,
  53. StripePriceID: "price_123",
  54. }))
  55. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  56. ID: "ti_444",
  57. Code: "business",
  58. Name: "Business",
  59. MessagesLimit: 2000,
  60. MessagesExpiryDuration: 10 * time.Hour,
  61. EmailsLimit: 123123,
  62. ReservationsLimit: 777333,
  63. AttachmentFileSizeLimit: 999111,
  64. AttachmentTotalSizeLimit: 888111,
  65. AttachmentExpiryDuration: time.Hour,
  66. StripePriceID: "price_456",
  67. }))
  68. response := request(t, s, "GET", "/v1/tiers", "", nil)
  69. require.Equal(t, 200, response.Code)
  70. var tiers []apiAccountBillingTier
  71. require.Nil(t, json.NewDecoder(response.Body).Decode(&tiers))
  72. require.Equal(t, 3, len(tiers))
  73. // Free tier
  74. tier := tiers[0]
  75. require.Equal(t, "", tier.Code)
  76. require.Equal(t, "", tier.Name)
  77. require.Equal(t, "ip", tier.Limits.Basis)
  78. require.Equal(t, int64(0), tier.Limits.Reservations)
  79. require.Equal(t, int64(2), tier.Limits.Messages) // :-(
  80. require.Equal(t, int64(13*3600), tier.Limits.MessagesExpiryDuration)
  81. require.Equal(t, int64(24), tier.Limits.Emails)
  82. require.Equal(t, int64(111), tier.Limits.AttachmentFileSize)
  83. require.Equal(t, int64(222), tier.Limits.AttachmentTotalSize)
  84. require.Equal(t, int64(123), tier.Limits.AttachmentExpiryDuration)
  85. // Admin tier is not included, because it is not paid!
  86. tier = tiers[1]
  87. require.Equal(t, "pro", tier.Code)
  88. require.Equal(t, "Pro", tier.Name)
  89. require.Equal(t, "tier", tier.Limits.Basis)
  90. require.Equal(t, int64(777), tier.Limits.Reservations)
  91. require.Equal(t, int64(1000), tier.Limits.Messages)
  92. require.Equal(t, int64(3600), tier.Limits.MessagesExpiryDuration)
  93. require.Equal(t, int64(123), tier.Limits.Emails)
  94. require.Equal(t, int64(999), tier.Limits.AttachmentFileSize)
  95. require.Equal(t, int64(888), tier.Limits.AttachmentTotalSize)
  96. require.Equal(t, int64(60), tier.Limits.AttachmentExpiryDuration)
  97. tier = tiers[2]
  98. require.Equal(t, "business", tier.Code)
  99. require.Equal(t, "Business", tier.Name)
  100. require.Equal(t, "tier", tier.Limits.Basis)
  101. require.Equal(t, int64(777333), tier.Limits.Reservations)
  102. require.Equal(t, int64(2000), tier.Limits.Messages)
  103. require.Equal(t, int64(36000), tier.Limits.MessagesExpiryDuration)
  104. require.Equal(t, int64(123123), tier.Limits.Emails)
  105. require.Equal(t, int64(999111), tier.Limits.AttachmentFileSize)
  106. require.Equal(t, int64(888111), tier.Limits.AttachmentTotalSize)
  107. require.Equal(t, int64(3600), tier.Limits.AttachmentExpiryDuration)
  108. }
  109. func TestPayments_SubscriptionCreate_NotAStripeCustomer_Success(t *testing.T) {
  110. stripeMock := &testStripeAPI{}
  111. defer stripeMock.AssertExpectations(t)
  112. c := newTestConfigWithAuthFile(t)
  113. c.StripeSecretKey = "secret key"
  114. c.StripeWebhookKey = "webhook key"
  115. s := newTestServer(t, c)
  116. s.stripe = stripeMock
  117. // Define how the mock should react
  118. stripeMock.
  119. On("NewCheckoutSession", mock.Anything).
  120. Return(&stripe.CheckoutSession{URL: "https://billing.stripe.com/abc/def"}, nil)
  121. // Create tier and user
  122. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  123. ID: "ti_123",
  124. Code: "pro",
  125. StripePriceID: "price_123",
  126. }))
  127. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  128. // Create subscription
  129. response := request(t, s, "POST", "/v1/account/billing/subscription", `{"tier": "pro"}`, map[string]string{
  130. "Authorization": util.BasicAuth("phil", "phil"),
  131. })
  132. require.Equal(t, 200, response.Code)
  133. redirectResponse, err := util.UnmarshalJSON[apiAccountBillingSubscriptionCreateResponse](io.NopCloser(response.Body))
  134. require.Nil(t, err)
  135. require.Equal(t, "https://billing.stripe.com/abc/def", redirectResponse.RedirectURL)
  136. }
  137. func TestPayments_SubscriptionCreate_StripeCustomer_Success(t *testing.T) {
  138. stripeMock := &testStripeAPI{}
  139. defer stripeMock.AssertExpectations(t)
  140. c := newTestConfigWithAuthFile(t)
  141. c.StripeSecretKey = "secret key"
  142. c.StripeWebhookKey = "webhook key"
  143. s := newTestServer(t, c)
  144. s.stripe = stripeMock
  145. // Define how the mock should react
  146. stripeMock.
  147. On("GetCustomer", "acct_123").
  148. Return(&stripe.Customer{Subscriptions: &stripe.SubscriptionList{}}, nil)
  149. stripeMock.
  150. On("NewCheckoutSession", mock.Anything).
  151. Return(&stripe.CheckoutSession{URL: "https://billing.stripe.com/abc/def"}, nil)
  152. // Create tier and user
  153. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  154. ID: "ti_123",
  155. Code: "pro",
  156. StripePriceID: "price_123",
  157. }))
  158. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  159. u, err := s.userManager.User("phil")
  160. require.Nil(t, err)
  161. billing := &user.Billing{
  162. StripeCustomerID: "acct_123",
  163. }
  164. require.Nil(t, s.userManager.ChangeBilling(u.Name, billing))
  165. // Create subscription
  166. response := request(t, s, "POST", "/v1/account/billing/subscription", `{"tier": "pro"}`, map[string]string{
  167. "Authorization": util.BasicAuth("phil", "phil"),
  168. })
  169. require.Equal(t, 200, response.Code)
  170. redirectResponse, err := util.UnmarshalJSON[apiAccountBillingSubscriptionCreateResponse](io.NopCloser(response.Body))
  171. require.Nil(t, err)
  172. require.Equal(t, "https://billing.stripe.com/abc/def", redirectResponse.RedirectURL)
  173. }
  174. func TestPayments_AccountDelete_Cancels_Subscription(t *testing.T) {
  175. stripeMock := &testStripeAPI{}
  176. defer stripeMock.AssertExpectations(t)
  177. c := newTestConfigWithAuthFile(t)
  178. c.EnableSignup = true
  179. c.StripeSecretKey = "secret key"
  180. c.StripeWebhookKey = "webhook key"
  181. s := newTestServer(t, c)
  182. s.stripe = stripeMock
  183. // Define how the mock should react
  184. stripeMock.
  185. On("CancelSubscription", "sub_123").
  186. Return(&stripe.Subscription{}, nil)
  187. // Create tier and user
  188. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  189. ID: "ti_123",
  190. Code: "pro",
  191. StripePriceID: "price_123",
  192. }))
  193. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  194. u, err := s.userManager.User("phil")
  195. require.Nil(t, err)
  196. billing := &user.Billing{
  197. StripeCustomerID: "acct_123",
  198. StripeSubscriptionID: "sub_123",
  199. }
  200. require.Nil(t, s.userManager.ChangeBilling(u.Name, billing))
  201. // Delete account
  202. rr := request(t, s, "DELETE", "/v1/account", `{"password": "phil"}`, map[string]string{
  203. "Authorization": util.BasicAuth("phil", "phil"),
  204. })
  205. require.Equal(t, 200, rr.Code)
  206. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  207. "Authorization": util.BasicAuth("phil", "mypass"),
  208. })
  209. require.Equal(t, 401, rr.Code)
  210. }
  211. func TestPayments_Checkout_Success_And_Increase_Ratelimits_Reset_Visitor(t *testing.T) {
  212. // This tests a successful checkout flow (not a paying customer -> paying customer),
  213. // and also tests that during the upgrade we are RESETTING THE RATE LIMITS of the existing user.
  214. stripeMock := &testStripeAPI{}
  215. defer stripeMock.AssertExpectations(t)
  216. c := newTestConfigWithAuthFile(t)
  217. c.StripeSecretKey = "secret key"
  218. c.StripeWebhookKey = "webhook key"
  219. c.VisitorRequestLimitBurst = 10
  220. c.VisitorRequestLimitReplenish = time.Hour
  221. s := newTestServer(t, c)
  222. s.stripe = stripeMock
  223. // Create a user with a Stripe subscription and 3 reservations
  224. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  225. ID: "ti_123",
  226. Code: "starter",
  227. StripePriceID: "price_1234",
  228. ReservationsLimit: 1,
  229. MessagesLimit: 100,
  230. MessagesExpiryDuration: time.Hour,
  231. }))
  232. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser)) // No tier
  233. u, err := s.userManager.User("phil")
  234. require.Nil(t, err)
  235. // Define how the mock should react
  236. stripeMock.
  237. On("GetSession", "SOMETOKEN").
  238. Return(&stripe.CheckoutSession{
  239. ClientReferenceID: u.ID, // ntfy user ID
  240. Customer: &stripe.Customer{
  241. ID: "acct_5555",
  242. },
  243. Subscription: &stripe.Subscription{
  244. ID: "sub_1234",
  245. },
  246. }, nil)
  247. stripeMock.
  248. On("GetSubscription", "sub_1234").
  249. Return(&stripe.Subscription{
  250. ID: "sub_1234",
  251. Status: stripe.SubscriptionStatusActive,
  252. CurrentPeriodEnd: 123456789,
  253. CancelAt: 0,
  254. Items: &stripe.SubscriptionItemList{
  255. Data: []*stripe.SubscriptionItem{
  256. {
  257. Price: &stripe.Price{ID: "price_1234"},
  258. },
  259. },
  260. },
  261. }, nil)
  262. stripeMock.
  263. On("UpdateCustomer", mock.Anything).
  264. Return(&stripe.Customer{}, nil)
  265. // Send messages until rate limit of free tier is hit
  266. for i := 0; i < 10; i++ {
  267. rr := request(t, s, "PUT", "/mytopic", "some message", map[string]string{
  268. "Authorization": util.BasicAuth("phil", "phil"),
  269. })
  270. require.Equal(t, 200, rr.Code)
  271. }
  272. rr := request(t, s, "PUT", "/mytopic", "some message", map[string]string{
  273. "Authorization": util.BasicAuth("phil", "phil"),
  274. })
  275. require.Equal(t, 429, rr.Code)
  276. // Simulate Stripe success return URL call (no user context)
  277. rr = request(t, s, "GET", "/v1/account/billing/subscription/success/SOMETOKEN", "", nil)
  278. require.Equal(t, 303, rr.Code)
  279. // Verify that database columns were updated
  280. u, err = s.userManager.User("phil")
  281. require.Nil(t, err)
  282. require.Equal(t, "starter", u.Tier.Code) // Not "pro"
  283. require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
  284. require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID)
  285. require.Equal(t, stripe.SubscriptionStatusActive, u.Billing.StripeSubscriptionStatus)
  286. require.Equal(t, int64(123456789), u.Billing.StripeSubscriptionPaidUntil.Unix())
  287. require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
  288. // FIXME FIXME This test is broken, because the rate limit logic is unclear!
  289. // Now for the fun part: Verify that new rate limits are immediately applied
  290. for i := 0; i < 100; i++ {
  291. rr := request(t, s, "PUT", "/mytopic", "some message", map[string]string{
  292. "Authorization": util.BasicAuth("phil", "phil"),
  293. })
  294. require.Equal(t, 200, rr.Code, "failed on iteration %d", i)
  295. }
  296. rr = request(t, s, "PUT", "/mytopic", "some message", map[string]string{
  297. "Authorization": util.BasicAuth("phil", "phil"),
  298. })
  299. require.Equal(t, 429, rr.Code)
  300. }
  301. func TestPayments_Webhook_Subscription_Updated_Downgrade_From_PastDue_To_Active(t *testing.T) {
  302. // This tests incoming webhooks from Stripe to update a subscription:
  303. // - All Stripe columns are updated in the user table
  304. // - When downgrading, excess reservations are deleted, including messages and attachments in
  305. // the corresponding topics
  306. stripeMock := &testStripeAPI{}
  307. defer stripeMock.AssertExpectations(t)
  308. c := newTestConfigWithAuthFile(t)
  309. c.StripeSecretKey = "secret key"
  310. c.StripeWebhookKey = "webhook key"
  311. s := newTestServer(t, c)
  312. s.stripe = stripeMock
  313. // Define how the mock should react
  314. stripeMock.
  315. On("ConstructWebhookEvent", mock.Anything, "stripe signature", "webhook key").
  316. Return(jsonToStripeEvent(t, subscriptionUpdatedEventJSON), nil)
  317. // Create a user with a Stripe subscription and 3 reservations
  318. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  319. ID: "ti_1",
  320. Code: "starter",
  321. StripePriceID: "price_1234", // !
  322. ReservationsLimit: 1, // !
  323. MessagesLimit: 100,
  324. MessagesExpiryDuration: time.Hour,
  325. AttachmentExpiryDuration: time.Hour,
  326. AttachmentFileSizeLimit: 1000000,
  327. AttachmentTotalSizeLimit: 1000000,
  328. AttachmentBandwidthLimit: 1000000,
  329. }))
  330. require.Nil(t, s.userManager.CreateTier(&user.Tier{
  331. ID: "ti_2",
  332. Code: "pro",
  333. StripePriceID: "price_1111", // !
  334. ReservationsLimit: 3, // !
  335. MessagesLimit: 200,
  336. MessagesExpiryDuration: time.Hour,
  337. AttachmentExpiryDuration: time.Hour,
  338. AttachmentFileSizeLimit: 1000000,
  339. AttachmentTotalSizeLimit: 1000000,
  340. AttachmentBandwidthLimit: 1000000,
  341. }))
  342. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  343. require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
  344. require.Nil(t, s.userManager.AddReservation("phil", "atopic", user.PermissionDenyAll))
  345. require.Nil(t, s.userManager.AddReservation("phil", "ztopic", user.PermissionDenyAll))
  346. // Add billing details
  347. u, err := s.userManager.User("phil")
  348. require.Nil(t, err)
  349. billing := &user.Billing{
  350. StripeCustomerID: "acct_5555",
  351. StripeSubscriptionID: "sub_1234",
  352. StripeSubscriptionStatus: stripe.SubscriptionStatusPastDue,
  353. StripeSubscriptionPaidUntil: time.Unix(123, 0),
  354. StripeSubscriptionCancelAt: time.Unix(456, 0),
  355. }
  356. require.Nil(t, s.userManager.ChangeBilling(u.Name, billing))
  357. // Add some messages to "atopic" and "ztopic", everything in "ztopic" will be deleted
  358. rr := request(t, s, "PUT", "/atopic", "some aaa message", map[string]string{
  359. "Authorization": util.BasicAuth("phil", "phil"),
  360. })
  361. require.Equal(t, 200, rr.Code)
  362. rr = request(t, s, "PUT", "/atopic", strings.Repeat("a", 5000), map[string]string{
  363. "Authorization": util.BasicAuth("phil", "phil"),
  364. })
  365. require.Equal(t, 200, rr.Code)
  366. a2 := toMessage(t, rr.Body.String())
  367. require.FileExists(t, filepath.Join(s.config.AttachmentCacheDir, a2.ID))
  368. rr = request(t, s, "PUT", "/ztopic", "some zzz message", map[string]string{
  369. "Authorization": util.BasicAuth("phil", "phil"),
  370. })
  371. require.Equal(t, 200, rr.Code)
  372. rr = request(t, s, "PUT", "/ztopic", strings.Repeat("z", 5000), map[string]string{
  373. "Authorization": util.BasicAuth("phil", "phil"),
  374. })
  375. require.Equal(t, 200, rr.Code)
  376. z2 := toMessage(t, rr.Body.String())
  377. require.FileExists(t, filepath.Join(s.config.AttachmentCacheDir, z2.ID))
  378. // Call the webhook: This does all the magic
  379. rr = request(t, s, "POST", "/v1/account/billing/webhook", "dummy", map[string]string{
  380. "Stripe-Signature": "stripe signature",
  381. })
  382. require.Equal(t, 200, rr.Code)
  383. // Verify that database columns were updated
  384. u, err = s.userManager.User("phil")
  385. require.Nil(t, err)
  386. require.Equal(t, "starter", u.Tier.Code) // Not "pro"
  387. require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
  388. require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID)
  389. require.Equal(t, stripe.SubscriptionStatusActive, u.Billing.StripeSubscriptionStatus) // Not "past_due"
  390. require.Equal(t, int64(1674268231), u.Billing.StripeSubscriptionPaidUntil.Unix()) // Updated
  391. require.Equal(t, int64(1674299999), u.Billing.StripeSubscriptionCancelAt.Unix()) // Updated
  392. // Verify that reservations were deleted
  393. r, err := s.userManager.Reservations("phil")
  394. require.Nil(t, err)
  395. require.Equal(t, 1, len(r)) // "ztopic" reservation was deleted
  396. require.Equal(t, "atopic", r[0].Topic)
  397. // Verify that messages and attachments were deleted
  398. time.Sleep(time.Second)
  399. s.execManager()
  400. ms, err := s.messageCache.Messages("atopic", sinceAllMessages, false)
  401. require.Nil(t, err)
  402. require.Equal(t, 2, len(ms))
  403. require.FileExists(t, filepath.Join(s.config.AttachmentCacheDir, a2.ID))
  404. ms, err = s.messageCache.Messages("ztopic", sinceAllMessages, false)
  405. require.Nil(t, err)
  406. require.Equal(t, 0, len(ms))
  407. require.NoFileExists(t, filepath.Join(s.config.AttachmentCacheDir, z2.ID))
  408. }
  409. type testStripeAPI struct {
  410. mock.Mock
  411. }
  412. var _ stripeAPI = (*testStripeAPI)(nil)
  413. func (s *testStripeAPI) NewCheckoutSession(params *stripe.CheckoutSessionParams) (*stripe.CheckoutSession, error) {
  414. args := s.Called(params)
  415. return args.Get(0).(*stripe.CheckoutSession), args.Error(1)
  416. }
  417. func (s *testStripeAPI) NewPortalSession(params *stripe.BillingPortalSessionParams) (*stripe.BillingPortalSession, error) {
  418. args := s.Called(params)
  419. return args.Get(0).(*stripe.BillingPortalSession), args.Error(1)
  420. }
  421. func (s *testStripeAPI) ListPrices(params *stripe.PriceListParams) ([]*stripe.Price, error) {
  422. args := s.Called(params)
  423. return args.Get(0).([]*stripe.Price), args.Error(1)
  424. }
  425. func (s *testStripeAPI) GetCustomer(id string) (*stripe.Customer, error) {
  426. args := s.Called(id)
  427. return args.Get(0).(*stripe.Customer), args.Error(1)
  428. }
  429. func (s *testStripeAPI) GetSession(id string) (*stripe.CheckoutSession, error) {
  430. args := s.Called(id)
  431. return args.Get(0).(*stripe.CheckoutSession), args.Error(1)
  432. }
  433. func (s *testStripeAPI) GetSubscription(id string) (*stripe.Subscription, error) {
  434. args := s.Called(id)
  435. return args.Get(0).(*stripe.Subscription), args.Error(1)
  436. }
  437. func (s *testStripeAPI) UpdateCustomer(id string, params *stripe.CustomerParams) (*stripe.Customer, error) {
  438. args := s.Called(id)
  439. return args.Get(0).(*stripe.Customer), args.Error(1)
  440. }
  441. func (s *testStripeAPI) UpdateSubscription(id string, params *stripe.SubscriptionParams) (*stripe.Subscription, error) {
  442. args := s.Called(id)
  443. return args.Get(0).(*stripe.Subscription), args.Error(1)
  444. }
  445. func (s *testStripeAPI) CancelSubscription(id string) (*stripe.Subscription, error) {
  446. args := s.Called(id)
  447. return args.Get(0).(*stripe.Subscription), args.Error(1)
  448. }
  449. func (s *testStripeAPI) ConstructWebhookEvent(payload []byte, header string, secret string) (stripe.Event, error) {
  450. args := s.Called(payload, header, secret)
  451. return args.Get(0).(stripe.Event), args.Error(1)
  452. }
  453. func jsonToStripeEvent(t *testing.T, v string) stripe.Event {
  454. var e stripe.Event
  455. if err := json.Unmarshal([]byte(v), &e); err != nil {
  456. t.Fatal(err)
  457. }
  458. return e
  459. }
  460. const subscriptionUpdatedEventJSON = `
  461. {
  462. "type": "customer.subscription.updated",
  463. "data": {
  464. "object": {
  465. "id": "sub_1234",
  466. "customer": "acct_5555",
  467. "status": "active",
  468. "current_period_end": 1674268231,
  469. "cancel_at": 1674299999,
  470. "items": {
  471. "data": [
  472. {
  473. "price": {
  474. "id": "price_1234"
  475. }
  476. }
  477. ]
  478. }
  479. }
  480. }
  481. }`