Create Temporal billing sweep schedule, workflow and activities and register them in the worker/start initialization. Add an HTMX preview route and banner for plan switches (switch button now GETs a preview; Confirm posts the switch). Extend the Stripe test mock to support invoice previews and add integration tests for PreviewSwitch behavior.
84 lines
2.8 KiB
Go
84 lines
2.8 KiB
Go
// Package stripetest provides an in-process stripe.Backend that returns canned
|
|
// subscription data. It lets tests exercise the fulfillment reconciler's
|
|
// "refetch from the Stripe API" path deterministically and offline, without a
|
|
// live Stripe account.
|
|
package stripetest
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
|
|
stripe "github.com/stripe/stripe-go/v81"
|
|
"github.com/stripe/stripe-go/v81/form"
|
|
)
|
|
|
|
// MockBackend implements stripe.Backend, returning canned data for subscription
|
|
// Get (GET /v1/subscriptions/{id}) and List (GET /v1/subscriptions). Set Err to
|
|
// make every call fail.
|
|
type MockBackend struct {
|
|
Sub *stripe.Subscription // returned for Get
|
|
List []*stripe.Subscription // returned for List
|
|
Invoice *stripe.Invoice // returned for invoice create-preview / upcoming
|
|
Err error // if set, every call returns it
|
|
}
|
|
|
|
func (m *MockBackend) Call(method, path, key string, params stripe.ParamsContainer, v stripe.LastResponseSetter) error {
|
|
if m.Err != nil {
|
|
return m.Err
|
|
}
|
|
var payload any
|
|
switch {
|
|
case path == "/v1/subscriptions":
|
|
payload = &stripe.SubscriptionList{Data: m.List}
|
|
case strings.HasPrefix(path, "/v1/subscriptions/"):
|
|
payload = m.Sub
|
|
case path == "/v1/invoices/create_preview" || path == "/v1/invoices/upcoming":
|
|
if m.Invoice == nil {
|
|
return errors.New("stripetest: no invoice preview configured")
|
|
}
|
|
payload = m.Invoice
|
|
default:
|
|
return errors.New("stripetest: unexpected path " + path)
|
|
}
|
|
b, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := json.Unmarshal(b, v); err != nil {
|
|
return err
|
|
}
|
|
v.SetLastResponse(&stripe.APIResponse{})
|
|
return nil
|
|
}
|
|
|
|
func (m *MockBackend) CallRaw(method, path, key string, body *form.Values, params *stripe.Params, v stripe.LastResponseSetter) error {
|
|
return m.Call(method, path, key, params, v)
|
|
}
|
|
|
|
func (m *MockBackend) CallStreaming(method, path, key string, params stripe.ParamsContainer, v stripe.StreamingLastResponseSetter) error {
|
|
return errors.New("stripetest: streaming not supported")
|
|
}
|
|
|
|
func (m *MockBackend) CallMultipart(method, path, key, boundary string, body *bytes.Buffer, params *stripe.Params, v stripe.LastResponseSetter) error {
|
|
return errors.New("stripetest: multipart not supported")
|
|
}
|
|
|
|
func (m *MockBackend) SetMaxNetworkRetries(_ int64) {}
|
|
|
|
// Install points stripe-go at m (with a dummy API key) and returns a function
|
|
// that restores the previous backend and key. Usage:
|
|
//
|
|
// defer stripetest.Install(m)() // or: t.Cleanup(stripetest.Install(m))
|
|
func Install(m *MockBackend) func() {
|
|
prevBackend := stripe.GetBackend(stripe.APIBackend)
|
|
prevKey := stripe.Key
|
|
stripe.SetBackend(stripe.APIBackend, m)
|
|
stripe.Key = "sk_test_mock"
|
|
return func() {
|
|
stripe.SetBackend(stripe.APIBackend, prevBackend)
|
|
stripe.Key = prevKey
|
|
}
|
|
}
|