-
Notifications
You must be signed in to change notification settings - Fork 0
/
billing.go
176 lines (148 loc) · 4.93 KB
/
billing.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package turso
import (
"context"
"fmt"
"io"
)
type BillingClient client
type Portal struct {
URL string `json:"url"`
}
func (c *BillingClient) Portal(ctx context.Context) (Portal, error) {
prefix := "/v1"
if c.client.Org != "" {
prefix = "/v1/organizations/" + c.client.Org
}
r, err := c.client.Post(ctx, prefix+"/billing/portal", nil)
if err != nil {
return Portal{}, fmt.Errorf("failed to get database usage: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return Portal{}, fmt.Errorf("failed to get billing portal with status %s: %v", r.Status, parseResponseError(r))
}
resp, err := unmarshal[struct{ Portal Portal }](r)
return resp.Portal, err
}
func (c *BillingClient) PortalForStripeId(ctx context.Context, stripeId string) (Portal, error) {
prefix := "/v1"
type Body struct {
StripeID string `json:"stripe_id"`
}
var body io.Reader
var err error
body, err = marshal(Body{StripeID: stripeId})
if err != nil {
return Portal{}, fmt.Errorf("could not serialize request body: %w", err)
}
r, err := c.client.Post(ctx, prefix+"/billing/portal", body)
if err != nil {
return Portal{}, fmt.Errorf("failed to get portal: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return Portal{}, fmt.Errorf("failed to get billing portal with status %s: %v", r.Status, parseResponseError(r))
}
resp, err := unmarshal[struct{ Portal Portal }](r)
return resp.Portal, err
}
func (c *BillingClient) HasPaymentMethod(ctx context.Context) (bool, error) {
prefix := "/v1"
if c.client.Org != "" {
prefix = "/v1/organizations/" + c.client.Org
}
r, err := c.client.Get(ctx, prefix+"/billing/payment-methods", nil)
if err != nil {
return false, fmt.Errorf("failed to get database usage: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return false, fmt.Errorf("failed to check payment method with status %s: %v", r.Status, parseResponseError(r))
}
resp, err := unmarshal[struct{ Exists bool }](r)
return resp.Exists, err
}
func (c *BillingClient) HasPaymentMethodWithStripeId(ctx context.Context, stripeId string) (bool, error) {
prefix := "/v1"
r, err := c.client.Get(ctx, fmt.Sprintf("%s/billing/payment-methods?stripe_id=%s", prefix, stripeId), nil)
if err != nil {
return false, fmt.Errorf("failed to check payment method: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return false, fmt.Errorf("failed to check payment method with status %s: %v", r.Status, parseResponseError(r))
}
resp, err := unmarshal[struct{ Exists bool }](r)
return resp.Exists, err
}
func (c *BillingClient) CreateStripeCustomer(ctx context.Context, name string) (string, error) {
prefix := "/v1"
type Body struct{ Name string }
body, err := marshal(Body{name})
if err != nil {
return "", fmt.Errorf("could not serialize request body: %w", err)
}
r, err := c.client.Post(ctx, prefix+"/organizations/stripe-customer", body)
if err != nil {
return "", fmt.Errorf("failed to create stripe customer: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return "", fmt.Errorf("failed to create stripe customer with status %s: %v", r.Status, parseResponseError(r))
}
resp, err := unmarshal[struct{ StripeCustomerId string }](r)
return resp.StripeCustomerId, err
}
type BillingAddress struct {
Line1 string `json:"line1"`
Line2 string `json:"line2"`
City string `json:"city"`
State string `json:"state"`
PostalCode string `json:"postal_code"`
Country string `json:"country"`
}
type TaxID struct {
Value string `json:"value"`
Country string `json:"country"`
}
type BillingCustomer struct {
Name string `json:"name"`
Email string `json:"email"`
TaxID TaxID `json:"tax_id"`
BillingAddress BillingAddress `json:"billing_address"`
}
func (c *BillingClient) GetBillingCustomer(ctx context.Context) (BillingCustomer, error) {
prefix := "/v1"
if c.client.Org != "" {
prefix = "/v1/organizations/" + c.client.Org
}
r, err := c.client.Get(ctx, prefix+"/billing/customer", nil)
if err != nil {
return BillingCustomer{}, fmt.Errorf("failed to get billing customer: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return BillingCustomer{}, fmt.Errorf("failed to get billing customer with status %s: %v", r.Status, parseResponseError(r))
}
resp, err := unmarshal[BillingCustomer](r)
return resp, err
}
func (c *BillingClient) UpdateBillingCustomer(ctx context.Context, customer BillingCustomer) error {
prefix := "/v1"
if c.client.Org != "" {
prefix = "/v1/organizations/" + c.client.Org
}
body, err := marshal(customer)
if err != nil {
return fmt.Errorf("could not serialize request body: %w", err)
}
r, err := c.client.Put(ctx, prefix+"/billing/customer", body)
if err != nil {
return fmt.Errorf("failed to update billing customer: %w", err)
}
defer r.Body.Close()
if r.StatusCode != 200 {
return fmt.Errorf("failed to update billing customer with status %s: %v", r.Status, parseResponseError(r))
}
return nil
}