• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stripe / stripe-go / 9687279319

26 Jun 2024 09:54PM UTC coverage: 63.947% (+0.06%) from 63.885%
9687279319

Pull #1877

github

stripe-openapi[bot]
Merge upstream and update generated code for v1102
Pull Request #1877: Update generated code for beta

0 of 3 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

6208 of 9708 relevant lines covered (63.95%)

16.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.07
/stripe.go
1
// Package stripe provides the binding for Stripe REST APIs.
2
package stripe
3

4
import (
5
        "bytes"
6
        "context"
7
        "crypto/tls"
8
        "crypto/x509"
9
        "encoding/json"
10
        "errors"
11
        "fmt"
12
        "io"
13
        "io/ioutil"
14
        "math/rand"
15
        "net/http"
16
        "net/url"
17
        "os/exec"
18
        "reflect"
19
        "regexp"
20
        "runtime"
21
        "strings"
22
        "sync"
23
        "time"
24

25
        "github.com/stripe/stripe-go/v78/form"
26
)
27

28
//
29
// Public constants
30
//
31

32
const (
33
        // APIBackend is a constant representing the API service backend.
34
        APIBackend SupportedBackend = "api"
35

36
        // APIURL is the URL of the API service backend.
37
        APIURL string = "https://api.stripe.com"
38

39
        // ClientVersion is the version of the stripe-go library being used.
40
        ClientVersion string = clientversion
41

42
        // ConnectURL is the URL for OAuth.
43
        ConnectURL string = "https://connect.stripe.com"
44

45
        // ConnectBackend is a constant representing the connect service backend for
46
        // OAuth.
47
        ConnectBackend SupportedBackend = "connect"
48

49
        // DefaultMaxNetworkRetries is the default maximum number of retries made
50
        // by a Stripe client.
51
        DefaultMaxNetworkRetries int64 = 2
52

53
        // UnknownPlatform is the string returned as the system name if we couldn't get
54
        // one from `uname`.
55
        UnknownPlatform string = "unknown platform"
56

57
        // UploadsBackend is a constant representing the uploads service backend.
58
        UploadsBackend SupportedBackend = "uploads"
59

60
        // UploadsURL is the URL of the uploads service backend.
61
        UploadsURL string = "https://files.stripe.com"
62
)
63

64
//
65
// Public variables
66
//
67

68
// EnableTelemetry is a global override for enabling client telemetry, which
69
// sends request performance metrics to Stripe via the `X-Stripe-Client-Telemetry`
70
// header. If set to true, all clients will send telemetry metrics. Defaults to
71
// true.
72
//
73
// Telemetry can also be disabled on a per-client basis by instead creating a
74
// `BackendConfig` with `EnableTelemetry: false`.
75
var EnableTelemetry = true
76

77
// Key is the Stripe API key used globally in the binding.
78
var Key string
79

80
// APIVersion is the currently supported API version
81
var APIVersion string = apiVersion
82

83
//
84
// Public types
85
//
86

87
// APIResponse encapsulates some common features of a response from the
88
// Stripe API.
89
type APIResponse struct {
90
        // Header contain a map of all HTTP header keys to values. Its behavior and
91
        // caveats are identical to that of http.Header.
92
        Header http.Header
93

94
        // IdempotencyKey contains the idempotency key used with this request.
95
        // Idempotency keys are a Stripe-specific concept that helps guarantee that
96
        // requests that fail and need to be retried are not duplicated.
97
        IdempotencyKey string
98

99
        // RawJSON contains the response body as raw bytes.
100
        RawJSON []byte
101

102
        // RequestID contains a string that uniquely identifies the Stripe request.
103
        // Used for debugging or support purposes.
104
        RequestID string
105

106
        // Status is a status code and message. e.g. "200 OK"
107
        Status string
108

109
        // StatusCode is a status code as integer. e.g. 200
110
        StatusCode int
111

112
        duration *time.Duration
113
}
114

115
// StreamingAPIResponse encapsulates some common features of a response from the
116
// Stripe API whose body can be streamed. This is used for "file downloads", and
117
// the `Body` property is an io.ReadCloser, so the user can stream it to another
118
// location such as a file or network request without buffering the entire body
119
// into memory.
120
type StreamingAPIResponse struct {
121
        Header         http.Header
122
        IdempotencyKey string
123
        Body           io.ReadCloser
124
        RequestID      string
125
        Status         string
126
        StatusCode     int
127
        duration       *time.Duration
128
}
129

130
func newAPIResponse(res *http.Response, resBody []byte, requestDuration *time.Duration) *APIResponse {
46✔
131
        return &APIResponse{
46✔
132
                Header:         res.Header,
46✔
133
                IdempotencyKey: res.Header.Get("Idempotency-Key"),
46✔
134
                RawJSON:        resBody,
46✔
135
                RequestID:      res.Header.Get("Request-Id"),
46✔
136
                Status:         res.Status,
46✔
137
                StatusCode:     res.StatusCode,
46✔
138
                duration:       requestDuration,
46✔
139
        }
46✔
140
}
46✔
141

142
func newStreamingAPIResponse(res *http.Response, body io.ReadCloser, requestDuration *time.Duration) *StreamingAPIResponse {
1✔
143
        return &StreamingAPIResponse{
1✔
144
                Header:         res.Header,
1✔
145
                IdempotencyKey: res.Header.Get("Idempotency-Key"),
1✔
146
                Body:           body,
1✔
147
                RequestID:      res.Header.Get("Request-Id"),
1✔
148
                Status:         res.Status,
1✔
149
                StatusCode:     res.StatusCode,
1✔
150
                duration:       requestDuration,
1✔
151
        }
1✔
152
}
1✔
153

154
// APIResource is a type assigned to structs that may come from Stripe API
155
// endpoints and contains facilities common to all of them.
156
type APIResource struct {
157
        LastResponse *APIResponse `json:"-"`
158
}
159

160
// APIStream is a type assigned to streaming responses that may come from Stripe API
161
type APIStream struct {
162
        LastResponse *StreamingAPIResponse
163
}
164

165
// SetLastResponse sets the HTTP response that returned the API resource.
166
func (r *APIResource) SetLastResponse(response *APIResponse) {
39✔
167
        r.LastResponse = response
39✔
168
}
39✔
169

170
// SetLastResponse sets the HTTP response that returned the API resource.
171
func (r *APIStream) SetLastResponse(response *StreamingAPIResponse) {
1✔
172
        r.LastResponse = response
1✔
173
}
1✔
174

175
// AppInfo contains information about the "app" which this integration belongs
176
// to. This should be reserved for plugins that wish to identify themselves
177
// with Stripe.
178
type AppInfo struct {
179
        Name      string `json:"name"`
180
        PartnerID string `json:"partner_id"`
181
        URL       string `json:"url"`
182
        Version   string `json:"version"`
183
}
184

185
// formatUserAgent formats an AppInfo in a way that's suitable to be appended
186
// to a User-Agent string. Note that this format is shared between all
187
// libraries so if it's changed, it should be changed everywhere.
188
func (a *AppInfo) formatUserAgent() string {
2✔
189
        str := a.Name
2✔
190
        if a.Version != "" {
4✔
191
                str += "/" + a.Version
2✔
192
        }
2✔
193
        if a.URL != "" {
4✔
194
                str += " (" + a.URL + ")"
2✔
195
        }
2✔
196
        return str
2✔
197
}
198

199
// Backend is an interface for making calls against a Stripe service.
200
// This interface exists to enable mocking for during testing if needed.
201
type Backend interface {
202
        Call(method, path, key string, params ParamsContainer, v LastResponseSetter) error
203
        CallStreaming(method, path, key string, params ParamsContainer, v StreamingLastResponseSetter) error
204
        CallRaw(method, path, key string, body *form.Values, params *Params, v LastResponseSetter) error
205
        CallMultipart(method, path, key, boundary string, body *bytes.Buffer, params *Params, v LastResponseSetter) error
206
        SetMaxNetworkRetries(maxNetworkRetries int64)
207
}
208

209
type RawRequestBackend interface {
210
        RawRequest(method, path, key, content string, params *RawParams) (*APIResponse, error)
211
}
212

213
// BackendConfig is used to configure a new Stripe backend.
214
type BackendConfig struct {
215
        // EnableTelemetry allows request metrics (request id and duration) to be sent
216
        // to Stripe in subsequent requests via the `X-Stripe-Client-Telemetry` header.
217
        //
218
        // This value is a pointer to allow us to differentiate an unset versus
219
        // empty value. Use stripe.Bool for an easy way to set this value.
220
        //
221
        // Defaults to false.
222
        EnableTelemetry *bool
223

224
        // HTTPClient is an HTTP client instance to use when making API requests.
225
        //
226
        // If left unset, it'll be set to a default HTTP client for the package.
227
        HTTPClient *http.Client
228

229
        // LeveledLogger is the logger that the backend will use to log errors,
230
        // warnings, and informational messages.
231
        //
232
        // LeveledLoggerInterface is implemented by LeveledLogger, and one can be
233
        // initialized at the desired level of logging.  LeveledLoggerInterface
234
        // also provides out-of-the-box compatibility with a Logrus Logger, but may
235
        // require a thin shim for use with other logging libraries that use less
236
        // standard conventions like Zap.
237
        //
238
        // Defaults to DefaultLeveledLogger.
239
        //
240
        // To set a logger that logs nothing, set this to a stripe.LeveledLogger
241
        // with a Level of LevelNull (simply setting this field to nil will not
242
        // work).
243
        LeveledLogger LeveledLoggerInterface
244

245
        // MaxNetworkRetries sets maximum number of times that the library will
246
        // retry requests that appear to have failed due to an intermittent
247
        // problem.
248
        //
249
        // This value is a pointer to allow us to differentiate an unset versus
250
        // empty value. Use stripe.Int64 for an easy way to set this value.
251
        //
252
        // Defaults to DefaultMaxNetworkRetries (2).
253
        MaxNetworkRetries *int64
254

255
        // URL is the base URL to use for API paths.
256
        //
257
        // This value is a pointer to allow us to differentiate an unset versus
258
        // empty value. Use stripe.String for an easy way to set this value.
259
        //
260
        // If left empty, it'll be set to the default for the SupportedBackend.
261
        URL *string
262
}
263

264
// BackendImplementation is the internal implementation for making HTTP calls
265
// to Stripe.
266
//
267
// The public use of this struct is deprecated. It will be unexported in a
268
// future version.
269
type BackendImplementation struct {
270
        Type              SupportedBackend
271
        URL               string
272
        HTTPClient        *http.Client
273
        LeveledLogger     LeveledLoggerInterface
274
        MaxNetworkRetries int64
275

276
        enableTelemetry bool
277

278
        // networkRetriesSleep indicates whether the backend should use the normal
279
        // sleep between retries.
280
        //
281
        // See also SetNetworkRetriesSleep.
282
        networkRetriesSleep bool
283

284
        requestMetricsBuffer chan requestMetrics
285
}
286

287
type metricsResponseSetter struct {
288
        LastResponseSetter
289
        backend *BackendImplementation
290
        params  *Params
291
}
292

293
func (s *metricsResponseSetter) SetLastResponse(response *APIResponse) {
32✔
294
        var usage []string
32✔
295
        if s.params != nil {
42✔
296
                usage = s.params.usage
10✔
297
        }
10✔
298
        s.backend.maybeEnqueueTelemetryMetrics(response.RequestID, response.duration, usage)
32✔
299
        s.LastResponseSetter.SetLastResponse(response)
32✔
300
}
301

302
func (s *metricsResponseSetter) UnmarshalJSON(b []byte) error {
32✔
303
        return json.Unmarshal(b, s.LastResponseSetter)
32✔
304
}
32✔
305

306
type streamingLastResponseSetterWrapper struct {
307
        StreamingLastResponseSetter
308
        f func(*StreamingAPIResponse)
309
}
310

311
func (l *streamingLastResponseSetterWrapper) SetLastResponse(response *StreamingAPIResponse) {
1✔
312
        l.f(response)
1✔
313
        l.StreamingLastResponseSetter.SetLastResponse(response)
1✔
314
}
1✔
315
func (l *streamingLastResponseSetterWrapper) UnmarshalJSON(b []byte) error {
×
316
        return json.Unmarshal(b, l.StreamingLastResponseSetter)
×
317
}
×
318

319
func extractParams(params ParamsContainer) (*form.Values, *Params, error) {
44✔
320
        var formValues *form.Values
44✔
321
        var commonParams *Params
44✔
322

44✔
323
        if params != nil {
61✔
324
                // This is a little unfortunate, but Go makes it impossible to compare
17✔
325
                // an interface value to nil without the use of the reflect package and
17✔
326
                // its true disciples insist that this is a feature and not a bug.
17✔
327
                //
17✔
328
                // Here we do invoke reflect because (1) we have to reflect anyway to
17✔
329
                // use encode with the form package, and (2) the corresponding removal
17✔
330
                // of boilerplate that this enables makes the small performance penalty
17✔
331
                // worth it.
17✔
332
                reflectValue := reflect.ValueOf(params)
17✔
333

17✔
334
                if reflectValue.Kind() == reflect.Ptr && !reflectValue.IsNil() {
32✔
335
                        commonParams = params.GetParams()
15✔
336

15✔
337
                        if !reflectValue.Elem().FieldByName("Metadata").IsZero() {
17✔
338
                                if commonParams.Metadata != nil {
3✔
339
                                        return nil, nil, fmt.Errorf("You cannot specify both the (deprecated) .Params.Metadata and .Metadata in %s", reflectValue.Elem().Type().Name())
1✔
340
                                }
1✔
341
                        }
342

343
                        if !reflectValue.Elem().FieldByName("Expand").IsZero() {
16✔
344
                                if commonParams.Expand != nil {
3✔
345
                                        return nil, nil, fmt.Errorf("You cannot specify both the (deprecated) .Params.Expand and .Expand in %s", reflectValue.Elem().Type().Name())
1✔
346
                                }
1✔
347
                        }
348

349
                        formValues = &form.Values{}
13✔
350
                        form.AppendTo(formValues, params)
13✔
351
                }
352
        }
353
        return formValues, commonParams, nil
42✔
354
}
355

356
// Call is the Backend.Call implementation for invoking Stripe APIs.
357
func (s *BackendImplementation) Call(method, path, key string, params ParamsContainer, v LastResponseSetter) error {
34✔
358
        body, commonParams, err := extractParams(params)
34✔
359
        if err != nil {
36✔
360
                return err
2✔
361
        }
2✔
362
        return s.CallRaw(method, path, key, body, commonParams, v)
32✔
363
}
364

365
// CallStreaming is the Backend.Call implementation for invoking Stripe APIs
366
// without buffering the response into memory.
367
func (s *BackendImplementation) CallStreaming(method, path, key string, params ParamsContainer, v StreamingLastResponseSetter) error {
3✔
368
        formValues, commonParams, err := extractParams(params)
3✔
369
        if err != nil {
3✔
370
                return err
×
371
        }
×
372

373
        var body string
3✔
374
        if formValues != nil && !formValues.Empty() {
3✔
375
                body = formValues.Encode()
×
376

×
377
                // On `GET`, move the payload into the URL
×
378
                if method == http.MethodGet {
×
379
                        path += "?" + body
×
380
                        body = ""
×
381
                }
×
382
        }
383
        bodyBuffer := bytes.NewBufferString(body)
3✔
384

3✔
385
        req, err := s.NewRequest(method, path, key, "application/x-www-form-urlencoded", commonParams)
3✔
386
        if err != nil {
3✔
387
                return err
×
388
        }
×
389

390
        responseSetter := streamingLastResponseSetterWrapper{
3✔
391
                v,
3✔
392
                func(response *StreamingAPIResponse) {
4✔
393
                        var usage []string
1✔
394
                        if commonParams != nil {
1✔
395
                                usage = commonParams.usage
×
396
                        }
×
397
                        s.maybeEnqueueTelemetryMetrics(response.RequestID, response.duration, usage)
1✔
398
                },
399
        }
400

401
        if err := s.DoStreaming(req, bodyBuffer, &responseSetter); err != nil {
5✔
402
                return err
2✔
403
        }
2✔
404

405
        return nil
1✔
406
}
407

408
// CallMultipart is the Backend.CallMultipart implementation for invoking Stripe APIs.
409
func (s *BackendImplementation) CallMultipart(method, path, key, boundary string, body *bytes.Buffer, params *Params, v LastResponseSetter) error {
×
410
        contentType := "multipart/form-data; boundary=" + boundary
×
411

×
412
        req, err := s.NewRequest(method, path, key, contentType, params)
×
413
        if err != nil {
×
414
                return err
×
415
        }
×
416

417
        if err := s.Do(req, body, v); err != nil {
×
418
                return err
×
419
        }
×
420

421
        return nil
×
422
}
423

424
// RawRequest is the Backend.RawRequest implementation for invoking Stripe APIs.
425
func (s *BackendImplementation) RawRequest(method, path, key, content string, params *RawParams) (*APIResponse, error) {
7✔
426
        var bodyBuffer = bytes.NewBuffer(nil)
7✔
427
        var commonParams *Params
7✔
428
        var err error
7✔
429
        var contentType string
7✔
430
        if method != http.MethodPost && method != http.MethodGet && method != http.MethodDelete {
7✔
431
                return nil, fmt.Errorf("method must be POST, GET, or DELETE. Received %s", method)
×
432
        }
×
433

434
        paramsIsNil := params == nil || reflect.ValueOf(params).IsNil()
7✔
435

7✔
436
        if paramsIsNil {
9✔
437
                _, commonParams, err = extractParams(params)
2✔
438
                if err != nil {
2✔
439
                        return nil, err
×
440
                }
×
441
                contentType = "application/x-www-form-urlencoded"
2✔
442
        } else {
5✔
443
                if params.APIMode == StandardAPIMode {
5✔
444
                        _, commonParams, err = extractParams(params)
×
445
                        if err != nil {
×
446
                                return nil, err
×
447
                        }
×
448
                        contentType = "application/x-www-form-urlencoded"
×
449
                } else if params.APIMode == PreviewAPIMode {
10✔
450
                        _, commonParams, err = extractParams(params)
5✔
451
                        if err != nil {
5✔
452
                                return nil, err
×
453
                        }
×
454
                        contentType = "application/json"
5✔
455
                } else {
×
456
                        return nil, fmt.Errorf("Unknown API mode %s", params.APIMode)
×
457
                }
×
458
        }
459

460
        bodyBuffer.WriteString(content)
7✔
461

7✔
462
        req, err := s.NewRequest(method, path, key, contentType, commonParams)
7✔
463

7✔
464
        if err != nil {
7✔
465
                return nil, err
×
466
        }
×
467

468
        if !paramsIsNil {
12✔
469
                if params.StripeContext != "" {
6✔
470
                        req.Header.Set("Stripe-Context", params.StripeContext)
1✔
471
                }
1✔
472
                if params.APIMode == PreviewAPIMode {
10✔
473
                        req.Header.Set("Stripe-Version", previewVersion)
5✔
474
                }
5✔
475
        }
476

477
        handleResponse := func(res *http.Response, err error) (interface{}, error) {
14✔
478
                return s.handleResponseBufferingErrors(res, err)
7✔
479
        }
7✔
480

481
        resp, result, requestDuration, err := s.requestWithRetriesAndTelemetry(req, bodyBuffer, handleResponse)
7✔
482
        if err != nil {
7✔
483
                return nil, err
×
484
        }
×
485
        requestID := resp.Header.Get("Request-Id")
7✔
486
        s.maybeEnqueueTelemetryMetrics(requestID, requestDuration, []string{"raw_request"})
7✔
487
        body, err := ioutil.ReadAll(result.(io.ReadCloser))
7✔
488
        if err != nil {
7✔
489
                return nil, err
×
490
        }
×
491
        return newAPIResponse(resp, body, requestDuration), nil
7✔
492
}
493

494
// CallRaw is the implementation for invoking Stripe APIs internally without a backend.
495
func (s *BackendImplementation) CallRaw(method, path, key string, form *form.Values, params *Params, v LastResponseSetter) error {
34✔
496
        var body string
34✔
497
        if form != nil && !form.Empty() {
42✔
498
                body = form.Encode()
8✔
499

8✔
500
                // On `GET`, move the payload into the URL
8✔
501
                if method != http.MethodPost {
12✔
502
                        path += "?" + body
4✔
503
                        body = ""
4✔
504
                }
4✔
505
        }
506
        bodyBuffer := bytes.NewBufferString(body)
34✔
507

34✔
508
        req, err := s.NewRequest(method, path, key, "application/x-www-form-urlencoded", params)
34✔
509
        if err != nil {
34✔
510
                return err
×
511
        }
×
512

513
        responseSetter := metricsResponseSetter{
34✔
514
                LastResponseSetter: v,
34✔
515
                backend:            s,
34✔
516
                params:             params,
34✔
517
        }
34✔
518

34✔
519
        if err := s.Do(req, bodyBuffer, &responseSetter); err != nil {
36✔
520
                return err
2✔
521
        }
2✔
522

523
        return nil
32✔
524
}
525

526
// NewRequest is used by Call to generate an http.Request. It handles encoding
527
// parameters and attaching the appropriate headers.
528
func (s *BackendImplementation) NewRequest(method, path, key, contentType string, params *Params) (*http.Request, error) {
68✔
529
        if !strings.HasPrefix(path, "/") {
88✔
530
                path = "/" + path
20✔
531
        }
20✔
532

533
        path = s.URL + path
68✔
534

68✔
535
        // Body is set later by `Do`.
68✔
536
        req, err := http.NewRequest(method, path, nil)
68✔
537
        if err != nil {
68✔
538
                s.LeveledLogger.Errorf("Cannot create Stripe request: %v", err)
×
539
                return nil, err
×
540
        }
×
541

542
        authorization := "Bearer " + key
68✔
543

68✔
544
        req.Header.Add("Authorization", authorization)
68✔
545
        req.Header.Add("Content-Type", contentType)
68✔
546
        req.Header.Add("Stripe-Version", APIVersion)
68✔
547
        req.Header.Add("User-Agent", encodedUserAgent)
68✔
548
        req.Header.Add("X-Stripe-Client-User-Agent", getEncodedStripeUserAgent())
68✔
549

68✔
550
        if params != nil {
86✔
551
                if params.Context != nil {
19✔
552
                        req = req.WithContext(params.Context)
1✔
553
                }
1✔
554

555
                if params.IdempotencyKey != nil {
19✔
556
                        idempotencyKey := strings.TrimSpace(*params.IdempotencyKey)
1✔
557
                        if len(idempotencyKey) > 255 {
1✔
558
                                return nil, errors.New("cannot use an idempotency key longer than 255 characters")
×
559
                        }
×
560

561
                        req.Header.Add("Idempotency-Key", idempotencyKey)
1✔
562
                } else if isHTTPWriteMethod(method) {
27✔
563
                        req.Header.Add("Idempotency-Key", NewIdempotencyKey())
10✔
564
                }
10✔
565

566
                if params.StripeAccount != nil {
19✔
567
                        req.Header.Add("Stripe-Account", strings.TrimSpace(*params.StripeAccount))
1✔
568
                }
1✔
569

570
                for k, v := range params.Headers {
19✔
571
                        for _, line := range v {
2✔
572
                                // Use Set to override the default value possibly set before
1✔
573
                                req.Header.Set(k, line)
1✔
574
                        }
1✔
575
                }
576
        }
577

578
        return req, nil
68✔
579
}
580

581
func (s *BackendImplementation) maybeSetTelemetryHeader(req *http.Request) {
48✔
582
        if s.enableTelemetry {
96✔
583
                select {
48✔
584
                case metrics := <-s.requestMetricsBuffer:
11✔
585
                        metricsJSON, err := json.Marshal(&requestTelemetry{LastRequestMetrics: metrics})
11✔
586
                        if err == nil {
22✔
587
                                req.Header.Set("X-Stripe-Client-Telemetry", string(metricsJSON))
11✔
588
                        } else {
11✔
589
                                s.LeveledLogger.Warnf("Unable to encode client telemetry: %v", err)
×
590
                        }
×
591
                default:
37✔
592
                        // There are no metrics available, so don't send any.
593
                        // This default case  needs to be here to prevent Do from blocking on an
594
                        // empty requestMetricsBuffer.
595
                }
596
        }
597
}
598

599
func (s *BackendImplementation) maybeEnqueueTelemetryMetrics(requestID string, requestDuration *time.Duration, usage []string) {
40✔
600
        if !s.enableTelemetry || requestID == "" {
50✔
601
                return
10✔
602
        }
10✔
603
        // If there's no duration to report and no usage to report, don't bother
604
        if requestDuration == nil && len(usage) == 0 {
30✔
605
                return
×
606
        }
×
607
        metrics := requestMetrics{
30✔
608
                RequestID: requestID,
30✔
609
        }
30✔
610
        if requestDuration != nil {
60✔
611
                requestDurationMS := int(*requestDuration / time.Millisecond)
30✔
612
                metrics.RequestDurationMS = &requestDurationMS
30✔
613
        }
30✔
614
        if len(usage) > 0 {
34✔
615
                metrics.Usage = usage
4✔
616
        }
4✔
617
        select {
30✔
618
        case s.requestMetricsBuffer <- metrics:
30✔
UNCOV
619
        default:
×
620
        }
621
}
622

623
func resetBodyReader(body *bytes.Buffer, req *http.Request) {
50✔
624
        // This might look a little strange, but we set the request's body
50✔
625
        // outside of `NewRequest` so that we can get a fresh version every
50✔
626
        // time.
50✔
627
        //
50✔
628
        // The background is that back in the era of old style HTTP, it was
50✔
629
        // safe to reuse `Request` objects, but with the addition of HTTP/2,
50✔
630
        // it's now only sometimes safe. Reusing a `Request` with a body will
50✔
631
        // break.
50✔
632
        //
50✔
633
        // See some details here:
50✔
634
        //
50✔
635
        //     https://github.com/golang/go/issues/19653#issuecomment-341539160
50✔
636
        //
50✔
637
        // And our original bug report here:
50✔
638
        //
50✔
639
        //     https://github.com/stripe/stripe-go/issues/642
50✔
640
        //
50✔
641
        // To workaround the problem, we put a fresh `Body` onto the `Request`
50✔
642
        // every time we execute it, and this seems to empirically resolve the
50✔
643
        // problem.
50✔
644
        if body != nil {
98✔
645
                // We can safely reuse the same buffer that we used to encode our body,
48✔
646
                // but return a new reader to it everytime so that each read is from
48✔
647
                // the beginning.
48✔
648
                reader := bytes.NewReader(body.Bytes())
48✔
649

48✔
650
                req.Body = nopReadCloser{reader}
48✔
651

48✔
652
                // And also add the same thing to `Request.GetBody`, which allows
48✔
653
                // `net/http` to get a new body in cases like a redirect. This is
48✔
654
                // usually not used, but it doesn't hurt to set it in case it's
48✔
655
                // needed. See:
48✔
656
                //
48✔
657
                //     https://github.com/stripe/stripe-go/issues/710
48✔
658
                //
48✔
659
                req.GetBody = func() (io.ReadCloser, error) {
48✔
660
                        reader := bytes.NewReader(body.Bytes())
×
661
                        return nopReadCloser{reader}, nil
×
662
                }
×
663
        }
664
}
665

666
// requestWithRetriesAndTelemetry uses s.HTTPClient to make an HTTP request,
667
// and handles retries, telemetry, and emitting log statements.  It attempts to
668
// avoid processing the *result* of the HTTP request. It receives a
669
// "handleResponse" func from the caller, and it defers to that to determine
670
// whether the request was a failure or success, and to convert the
671
// response/error into the appropriate type of error or an appropriate result
672
// type.
673
func (s *BackendImplementation) requestWithRetriesAndTelemetry(
674
        req *http.Request,
675
        body *bytes.Buffer,
676
        handleResponse func(*http.Response, error) (interface{}, error),
677
) (*http.Response, interface{}, *time.Duration, error) {
48✔
678
        s.LeveledLogger.Infof("Requesting %v %v%v", req.Method, req.URL.Host, req.URL.Path)
48✔
679
        s.maybeSetTelemetryHeader(req)
48✔
680
        var resp *http.Response
48✔
681
        var err error
48✔
682
        var requestDuration time.Duration
48✔
683
        var result interface{}
48✔
684
        for retry := 0; ; {
98✔
685
                start := time.Now()
50✔
686
                resetBodyReader(body, req)
50✔
687

50✔
688
                resp, err = s.HTTPClient.Do(req)
50✔
689

50✔
690
                requestDuration = time.Since(start)
50✔
691
                s.LeveledLogger.Infof("Request completed in %v (retry: %v)", requestDuration, retry)
50✔
692

50✔
693
                result, err = handleResponse(resp, err)
50✔
694

50✔
695
                // If the response was okay, or an error that shouldn't be retried,
50✔
696
                // we're done, and it's safe to leave the retry loop.
50✔
697
                shouldRetry, noRetryReason := s.shouldRetry(err, req, resp, retry)
50✔
698

50✔
699
                if !shouldRetry {
98✔
700
                        s.LeveledLogger.Infof("Not retrying request: %v", noRetryReason)
48✔
701
                        break
48✔
702
                }
703

704
                sleepDuration := s.sleepTime(retry)
2✔
705
                retry++
2✔
706

2✔
707
                s.LeveledLogger.Warnf("Initiating retry %v for request %v %v%v after sleeping %v",
2✔
708
                        retry, req.Method, req.URL.Host, req.URL.Path, sleepDuration)
2✔
709

2✔
710
                time.Sleep(sleepDuration)
2✔
711
        }
712

713
        if err != nil {
54✔
714
                return nil, nil, nil, err
6✔
715
        }
6✔
716

717
        return resp, result, &requestDuration, nil
42✔
718
}
719

720
func (s *BackendImplementation) logError(statusCode int, err error) {
4✔
721
        if stripeErr, ok := err.(*Error); ok {
7✔
722
                // The Stripe API makes a distinction between errors that were
3✔
723
                // caused by invalid parameters or something else versus those
3✔
724
                // that occurred *despite* valid parameters, the latter coming
3✔
725
                // back with status 402.
3✔
726
                //
3✔
727
                // On a 402, log to info so as to not make an integration's log
3✔
728
                // noisy with error messages that they don't have much control
3✔
729
                // over.
3✔
730
                //
3✔
731
                // Note I use the constant 402 instead of an `http.Status*`
3✔
732
                // constant because technically 402 is "Payment required". The
3✔
733
                // Stripe API doesn't comply to the letter of the specification
3✔
734
                // and uses it in a broader sense.
3✔
735
                if statusCode == 402 {
4✔
736
                        s.LeveledLogger.Infof("User-compelled request error from Stripe (status %v): %v",
1✔
737
                                statusCode, stripeErr.redact())
1✔
738
                } else {
3✔
739
                        s.LeveledLogger.Errorf("Request error from Stripe (status %v): %v",
2✔
740
                                statusCode, stripeErr.redact())
2✔
741
                }
2✔
742
        } else {
1✔
743
                s.LeveledLogger.Errorf("Error decoding error from Stripe: %v", err)
1✔
744
        }
1✔
745
}
746

747
func (s *BackendImplementation) handleResponseBufferingErrors(res *http.Response, err error) (io.ReadCloser, error) {
10✔
748
        // Some sort of connection error
10✔
749
        if err != nil {
10✔
750
                s.LeveledLogger.Errorf("Request failed with error: %v", err)
×
751
                return res.Body, err
×
752
        }
×
753

754
        // Successful response, return the body ReadCloser
755
        if res.StatusCode < 400 {
18✔
756
                return res.Body, err
8✔
757
        }
8✔
758

759
        // Failure: try and parse the json of the response
760
        // when logging the error
761
        var resBody []byte
2✔
762
        resBody, err = ioutil.ReadAll(res.Body)
2✔
763
        res.Body.Close()
2✔
764
        if err == nil {
4✔
765
                err = s.ResponseToError(res, resBody)
2✔
766
        } else {
2✔
767
                s.logError(res.StatusCode, err)
×
768
        }
×
769

770
        return res.Body, err
2✔
771
}
772

773
// DoStreaming is used by CallStreaming to execute an API request. It uses the
774
// backend's HTTP client to execure the request.  In successful cases, it sets
775
// a StreamingLastResponse onto v, but in unsuccessful cases handles unmarshaling
776
// errors returned by the API.
777
func (s *BackendImplementation) DoStreaming(req *http.Request, body *bytes.Buffer, v StreamingLastResponseSetter) error {
3✔
778
        handleResponse := func(res *http.Response, err error) (interface{}, error) {
6✔
779
                return s.handleResponseBufferingErrors(res, err)
3✔
780
        }
3✔
781

782
        resp, result, requestDuration, err := s.requestWithRetriesAndTelemetry(req, body, handleResponse)
3✔
783
        if err != nil {
5✔
784
                return err
2✔
785
        }
2✔
786
        v.SetLastResponse(newStreamingAPIResponse(resp, result.(io.ReadCloser), requestDuration))
1✔
787
        return nil
1✔
788
}
789

790
// Do is used by Call to execute an API request and parse the response. It uses
791
// the backend's HTTP client to execute the request and unmarshals the response
792
// into v. It also handles unmarshaling errors returned by the API.
793
func (s *BackendImplementation) Do(req *http.Request, body *bytes.Buffer, v LastResponseSetter) error {
38✔
794
        handleResponse := func(res *http.Response, err error) (interface{}, error) {
78✔
795
                var resBody []byte
40✔
796
                if err == nil {
78✔
797
                        resBody, err = ioutil.ReadAll(res.Body)
38✔
798
                        res.Body.Close()
38✔
799
                }
38✔
800

801
                if err != nil {
42✔
802
                        s.LeveledLogger.Errorf("Request failed with error: %v", err)
2✔
803
                } else if res.StatusCode >= 400 {
44✔
804
                        err = s.ResponseToError(res, resBody)
4✔
805

4✔
806
                        s.logError(res.StatusCode, err)
4✔
807
                }
4✔
808

809
                return resBody, err
40✔
810
        }
811

812
        res, result, requestDuration, err := s.requestWithRetriesAndTelemetry(req, body, handleResponse)
38✔
813
        if err != nil {
42✔
814
                return err
4✔
815
        }
4✔
816
        resBody := result.([]byte)
34✔
817
        s.LeveledLogger.Debugf("Response: %s", string(resBody))
34✔
818

34✔
819
        err = s.UnmarshalJSONVerbose(res.StatusCode, resBody, v)
34✔
820
        v.SetLastResponse(newAPIResponse(res, resBody, requestDuration))
34✔
821
        return err
34✔
822
}
823

824
// ResponseToError converts a stripe response to an Error.
825
func (s *BackendImplementation) ResponseToError(res *http.Response, resBody []byte) error {
7✔
826
        var raw rawError
7✔
827
        if s.Type == ConnectBackend {
7✔
828
                // If this is an OAuth request, deserialize as Error because OAuth errors
×
829
                // are a different shape from the standard API errors.
×
830
                var topLevelError Error
×
831
                if err := s.UnmarshalJSONVerbose(res.StatusCode, resBody, &topLevelError); err != nil {
×
832
                        return err
×
833
                }
×
834
                raw.Error = &topLevelError
×
835
        } else {
7✔
836
                if err := s.UnmarshalJSONVerbose(res.StatusCode, resBody, &raw); err != nil {
9✔
837
                        return err
2✔
838
                }
2✔
839
        }
840

841
        // no error in resBody
842
        if raw.Error == nil {
5✔
843
                err := errors.New(string(resBody))
×
844
                return err
×
845
        }
×
846
        raw.Error.HTTPStatusCode = res.StatusCode
5✔
847
        raw.Error.RequestID = res.Header.Get("Request-Id")
5✔
848

5✔
849
        var typedError error
5✔
850
        switch raw.Error.Type {
5✔
851
        case ErrorTypeAPI:
×
852
                typedError = &APIError{stripeErr: raw.Error}
×
853
        case ErrorTypeCard:
1✔
854
                cardErr := &CardError{stripeErr: raw.Error}
1✔
855

1✔
856
                // `DeclineCode` was traditionally only available on `CardError`, but
1✔
857
                // we ended up moving it to the top-level error as well. However, keep
1✔
858
                // it on `CardError` for backwards compatibility.
1✔
859
                if raw.Error.DeclineCode != "" {
2✔
860
                        cardErr.DeclineCode = raw.Error.DeclineCode
1✔
861
                }
1✔
862

863
                typedError = cardErr
1✔
864
        case ErrorTypeIdempotency:
×
865
                typedError = &IdempotencyError{stripeErr: raw.Error}
×
866
        case ErrorTypeInvalidRequest:
1✔
867
                typedError = &InvalidRequestError{stripeErr: raw.Error}
1✔
868
        }
869
        raw.Error.Err = typedError
5✔
870

5✔
871
        raw.Error.SetLastResponse(newAPIResponse(res, resBody, nil))
5✔
872

5✔
873
        return raw.Error
5✔
874
}
875

876
// SetMaxNetworkRetries sets max number of retries on failed requests
877
//
878
// This function is deprecated. Please use GetBackendWithConfig instead.
879
func (s *BackendImplementation) SetMaxNetworkRetries(maxNetworkRetries int64) {
×
880
        s.MaxNetworkRetries = maxNetworkRetries
×
881
}
×
882

883
// SetNetworkRetriesSleep allows the normal sleep between network retries to be
884
// enabled or disabled.
885
//
886
// This function is available for internal testing only and should never be
887
// used in production.
888
func (s *BackendImplementation) SetNetworkRetriesSleep(sleep bool) {
2✔
889
        s.networkRetriesSleep = sleep
2✔
890
}
2✔
891

892
// UnmarshalJSONVerbose unmarshals JSON, but in case of a failure logs and
893
// produces a more descriptive error.
894
func (s *BackendImplementation) UnmarshalJSONVerbose(statusCode int, body []byte, v interface{}) error {
44✔
895
        err := json.Unmarshal(body, v)
44✔
896
        if err != nil {
48✔
897
                // If we got invalid JSON back then something totally unexpected is
4✔
898
                // happening (caused by a bug on the server side). Put a sample of the
4✔
899
                // response body into the error message so we can get a better feel for
4✔
900
                // what the problem was.
4✔
901
                bodySample := string(body)
4✔
902
                if len(bodySample) > 500 {
5✔
903
                        bodySample = bodySample[0:500] + " ..."
1✔
904
                }
1✔
905

906
                // Make sure a multi-line response ends up all on one line
907
                bodySample = strings.Replace(bodySample, "\n", "\\n", -1)
4✔
908

4✔
909
                newErr := fmt.Errorf("Couldn't deserialize JSON (response status: %v, body sample: '%s'): %v",
4✔
910
                        statusCode, bodySample, err)
4✔
911
                s.LeveledLogger.Errorf("%s", newErr.Error())
4✔
912
                return newErr
4✔
913
        }
914

915
        return nil
40✔
916
}
917

918
// Regular expressions used to match a few error types that we know we don't
919
// want to retry. Unfortunately these errors aren't typed so we match on the
920
// error's message.
921
var (
922
        redirectsErrorRE = regexp.MustCompile(`stopped after \d+ redirects\z`)
923
        schemeErrorRE    = regexp.MustCompile(`unsupported protocol scheme`)
924
)
925

926
// Checks if an error is a problem that we should retry on. This includes both
927
// socket errors that may represent an intermittent problem and some special
928
// HTTP statuses.
929
//
930
// Returns a boolean indicating whether a client should retry. If false, a
931
// second string parameter is also returned with a short message indicating why
932
// no retry should occur. This can be used for logging/informational purposes.
933
func (s *BackendImplementation) shouldRetry(err error, req *http.Request, resp *http.Response, numRetries int) (bool, string) {
64✔
934
        if numRetries >= int(s.MaxNetworkRetries) {
102✔
935
                return false, "max retries exceeded"
38✔
936
        }
38✔
937

938
        stripeErr, _ := err.(*Error)
26✔
939

26✔
940
        // Don't retry if the context was canceled or its deadline was exceeded.
26✔
941
        if req.Context() != nil && req.Context().Err() != nil {
27✔
942
                switch req.Context().Err() {
1✔
943
                case context.Canceled:
1✔
944
                        return false, "context canceled"
1✔
945
                case context.DeadlineExceeded:
×
946
                        return false, "context deadline exceeded"
×
947
                default:
×
948
                        return false, fmt.Sprintf("unknown context error: %v", req.Context().Err())
×
949
                }
950
        }
951

952
        // We retry most errors that come out of HTTP requests except for a curated
953
        // list that we know not to be retryable. This list is probably not
954
        // exhaustive, so it'd be okay to add new errors to it. It'd also be okay to
955
        // flip this to an inverted strategy of retrying only errors that we know
956
        // to be retryable in a future refactor, if a good methodology is found for
957
        // identifying that full set of errors.
958
        if stripeErr == nil && err != nil {
31✔
959
                if urlErr, ok := err.(*url.Error); ok {
10✔
960
                        // Don't retry too many redirects.
4✔
961
                        if redirectsErrorRE.MatchString(urlErr.Error()) {
5✔
962
                                return false, urlErr.Error()
1✔
963
                        }
1✔
964

965
                        // Don't retry invalid protocol scheme.
966
                        if schemeErrorRE.MatchString(urlErr.Error()) {
4✔
967
                                return false, urlErr.Error()
1✔
968
                        }
1✔
969

970
                        // Don't retry TLS certificate validation problems.
971
                        if _, ok := urlErr.Err.(x509.UnknownAuthorityError); ok {
3✔
972
                                return false, urlErr.Error()
1✔
973
                        }
1✔
974
                }
975

976
                // Do retry every other type of non-Stripe error.
977
                return true, ""
3✔
978
        }
979

980
        // The API may ask us not to retry (e.g. if doing so would be a no-op), or
981
        // advise us to retry (e.g. in cases of lock timeouts). Defer to those
982
        // instructions if given.
983
        if resp.Header.Get("Stripe-Should-Retry") == "false" {
20✔
984
                return false, "`Stripe-Should-Retry` header returned `false`"
1✔
985
        }
1✔
986
        if resp.Header.Get("Stripe-Should-Retry") == "true" {
19✔
987
                return true, ""
1✔
988
        }
1✔
989

990
        // 409 Conflict
991
        if resp.StatusCode == http.StatusConflict {
18✔
992
                return true, ""
1✔
993
        }
1✔
994

995
        // 429 Too Many Requests
996
        //
997
        // There are a few different problems that can lead to a 429. The most
998
        // common is rate limiting, on which we *don't* want to retry because
999
        // that'd likely contribute to more contention problems. However, some 429s
1000
        // are lock timeouts, which is when a request conflicted with another
1001
        // request or an internal process on some particular object. These 429s are
1002
        // safe to retry.
1003
        if resp.StatusCode == http.StatusTooManyRequests {
18✔
1004
                if stripeErr != nil && stripeErr.Code == ErrorCodeLockTimeout {
3✔
1005
                        return true, ""
1✔
1006
                }
1✔
1007
        }
1008

1009
        // Retry on 500, 503, and other internal errors.
1010
        //
1011
        // Note that we expect the stripe-should-retry header to be false
1012
        // in most cases when a 500 is returned, since our idempotency framework
1013
        // would typically replay it anyway.
1014
        if resp.StatusCode >= http.StatusInternalServerError {
17✔
1015
                return true, ""
2✔
1016
        }
2✔
1017

1018
        return false, "response not known to be safe for retry"
13✔
1019
}
1020

1021
// sleepTime calculates sleeping/delay time in milliseconds between failure and a new one request.
1022
func (s *BackendImplementation) sleepTime(numRetries int) time.Duration {
2✔
1023
        // We disable sleeping in some cases for tests.
2✔
1024
        if !s.networkRetriesSleep {
4✔
1025
                return 0 * time.Second
2✔
1026
        }
2✔
1027

1028
        // Apply exponential backoff with minNetworkRetriesDelay on the
1029
        // number of num_retries so far as inputs.
1030
        delay := minNetworkRetriesDelay + minNetworkRetriesDelay*time.Duration(numRetries*numRetries)
×
1031

×
1032
        // Do not allow the number to exceed maxNetworkRetriesDelay.
×
1033
        if delay > maxNetworkRetriesDelay {
×
1034
                delay = maxNetworkRetriesDelay
×
1035
        }
×
1036

1037
        // Apply some jitter by randomizing the value in the range of 75%-100%.
1038
        jitter := rand.Int63n(int64(delay / 4))
×
1039
        delay -= time.Duration(jitter)
×
1040

×
1041
        // But never sleep less than the base sleep seconds.
×
1042
        if delay < minNetworkRetriesDelay {
×
1043
                delay = minNetworkRetriesDelay
×
1044
        }
×
1045

1046
        return delay
×
1047
}
1048

1049
// Backends are the currently supported endpoints.
1050
type Backends struct {
1051
        API, Connect, Uploads Backend
1052
        mu                    sync.RWMutex
1053
}
1054

1055
// LastResponseSetter defines a type that contains an HTTP response from a Stripe
1056
// API endpoint.
1057
type LastResponseSetter interface {
1058
        SetLastResponse(response *APIResponse)
1059
}
1060

1061
// StreamingLastResponseSetter defines a type that contains an HTTP response from a Stripe
1062
// API endpoint.
1063
type StreamingLastResponseSetter interface {
1064
        SetLastResponse(response *StreamingAPIResponse)
1065
}
1066

1067
// SupportedBackend is an enumeration of supported Stripe endpoints.
1068
// Currently supported values are "api" and "uploads".
1069
type SupportedBackend string
1070

1071
//
1072
// Public functions
1073
//
1074

1075
// Bool returns a pointer to the bool value passed in.
1076
func Bool(v bool) *bool {
21✔
1077
        return &v
21✔
1078
}
21✔
1079

1080
// BoolValue returns the value of the bool pointer passed in or
1081
// false if the pointer is nil.
1082
func BoolValue(v *bool) bool {
35✔
1083
        if v != nil {
50✔
1084
                return *v
15✔
1085
        }
15✔
1086
        return false
20✔
1087
}
1088

1089
// BoolSlice returns a slice of bool pointers given a slice of bools.
1090
func BoolSlice(v []bool) []*bool {
2✔
1091
        out := make([]*bool, len(v))
2✔
1092
        for i := range v {
6✔
1093
                out[i] = &v[i]
4✔
1094
        }
4✔
1095
        return out
2✔
1096
}
1097

1098
// Float64 returns a pointer to the float64 value passed in.
1099
func Float64(v float64) *float64 {
×
1100
        return &v
×
1101
}
×
1102

1103
// Float64Value returns the value of the float64 pointer passed in or
1104
// 0 if the pointer is nil.
1105
func Float64Value(v *float64) float64 {
×
1106
        if v != nil {
×
1107
                return *v
×
1108
        }
×
1109
        return 0
×
1110
}
1111

1112
// Float64Slice returns a slice of float64 pointers given a slice of float64s.
1113
func Float64Slice(v []float64) []*float64 {
2✔
1114
        out := make([]*float64, len(v))
2✔
1115
        for i := range v {
5✔
1116
                out[i] = &v[i]
3✔
1117
        }
3✔
1118
        return out
2✔
1119
}
1120

1121
// FormatURLPath takes a format string (of the kind used in the fmt package)
1122
// representing a URL path with a number of parameters that belong in the path
1123
// and returns a formatted string.
1124
//
1125
// This is mostly a pass through to Sprintf. It exists to make it
1126
// it impossible to accidentally provide a parameter type that would be
1127
// formatted improperly; for example, a string pointer instead of a string.
1128
//
1129
// It also URL-escapes every given parameter. This usually isn't necessary for
1130
// a standard Stripe ID, but is needed in places where user-provided IDs are
1131
// allowed, like in coupons or plans. We apply it broadly for extra safety.
1132
func FormatURLPath(format string, params ...string) string {
2✔
1133
        // Convert parameters to interface{} and URL-escape them
2✔
1134
        untypedParams := make([]interface{}, len(params))
2✔
1135
        for i, param := range params {
5✔
1136
                untypedParams[i] = interface{}(url.QueryEscape(param))
3✔
1137
        }
3✔
1138

1139
        return fmt.Sprintf(format, untypedParams...)
2✔
1140
}
1141

1142
// GetBackend returns one of the library's supported backends based off of the
1143
// given argument.
1144
//
1145
// It returns an existing default backend if one's already been created.
1146
func GetBackend(backendType SupportedBackend) Backend {
24✔
1147
        var backend Backend
24✔
1148

24✔
1149
        backends.mu.RLock()
24✔
1150
        switch backendType {
24✔
1151
        case APIBackend:
24✔
1152
                backend = backends.API
24✔
1153
        case ConnectBackend:
×
1154
                backend = backends.Connect
×
1155
        case UploadsBackend:
×
1156
                backend = backends.Uploads
×
1157
        }
1158
        backends.mu.RUnlock()
24✔
1159
        if backend != nil {
48✔
1160
                return backend
24✔
1161
        }
24✔
1162

1163
        backend = GetBackendWithConfig(
×
1164
                backendType,
×
1165
                &BackendConfig{
×
1166
                        HTTPClient:        httpClient,
×
1167
                        LeveledLogger:     nil, // Set by GetBackendWithConfiguation when nil
×
1168
                        MaxNetworkRetries: nil, // Set by GetBackendWithConfiguation when nil
×
1169
                        URL:               nil, // Set by GetBackendWithConfiguation when nil
×
1170
                },
×
1171
        )
×
1172

×
1173
        SetBackend(backendType, backend)
×
1174

×
1175
        return backend
×
1176
}
1177

1178
// GetBackendWithConfig is the same as GetBackend except that it can be given a
1179
// configuration struct that will configure certain aspects of the backend
1180
// that's return.
1181
func GetBackendWithConfig(backendType SupportedBackend, config *BackendConfig) Backend {
28✔
1182
        if config.HTTPClient == nil {
51✔
1183
                config.HTTPClient = httpClient
23✔
1184
        }
23✔
1185

1186
        if config.LeveledLogger == nil {
36✔
1187
                config.LeveledLogger = DefaultLeveledLogger
8✔
1188
        }
8✔
1189

1190
        if config.MaxNetworkRetries == nil {
39✔
1191
                config.MaxNetworkRetries = Int64(DefaultMaxNetworkRetries)
11✔
1192
        }
11✔
1193

1194
        switch backendType {
28✔
1195
        case APIBackend:
26✔
1196
                if config.URL == nil {
29✔
1197
                        config.URL = String(APIURL)
3✔
1198
                }
3✔
1199

1200
                config.URL = String(normalizeURL(*config.URL))
26✔
1201

26✔
1202
                return newBackendImplementation(backendType, config)
26✔
1203

1204
        case UploadsBackend:
1✔
1205
                if config.URL == nil {
2✔
1206
                        config.URL = String(UploadsURL)
1✔
1207
                }
1✔
1208

1209
                config.URL = String(normalizeURL(*config.URL))
1✔
1210

1✔
1211
                return newBackendImplementation(backendType, config)
1✔
1212

1213
        case ConnectBackend:
1✔
1214
                if config.URL == nil {
2✔
1215
                        config.URL = String(ConnectURL)
1✔
1216
                }
1✔
1217

1218
                config.URL = String(normalizeURL(*config.URL))
1✔
1219

1✔
1220
                return newBackendImplementation(backendType, config)
1✔
1221
        }
1222

1223
        return nil
×
1224
}
1225

1226
// Int64 returns a pointer to the int64 value passed in.
1227
func Int64(v int64) *int64 {
42✔
1228
        return &v
42✔
1229
}
42✔
1230

1231
// Int64Value returns the value of the int64 pointer passed in or
1232
// 0 if the pointer is nil.
1233
func Int64Value(v *int64) int64 {
4✔
1234
        if v != nil {
8✔
1235
                return *v
4✔
1236
        }
4✔
1237
        return 0
×
1238
}
1239

1240
// Int64Slice returns a slice of int64 pointers given a slice of int64s.
1241
func Int64Slice(v []int64) []*int64 {
2✔
1242
        out := make([]*int64, len(v))
2✔
1243
        for i := range v {
5✔
1244
                out[i] = &v[i]
3✔
1245
        }
3✔
1246
        return out
2✔
1247
}
1248

1249
// NewBackends creates a new set of backends with the given HTTP client.
1250
func NewBackends(httpClient *http.Client) *Backends {
1✔
1251
        apiConfig := &BackendConfig{HTTPClient: httpClient}
1✔
1252
        connectConfig := &BackendConfig{HTTPClient: httpClient}
1✔
1253
        uploadConfig := &BackendConfig{HTTPClient: httpClient}
1✔
1254
        return &Backends{
1✔
1255
                API:     GetBackendWithConfig(APIBackend, apiConfig),
1✔
1256
                Connect: GetBackendWithConfig(ConnectBackend, connectConfig),
1✔
1257
                Uploads: GetBackendWithConfig(UploadsBackend, uploadConfig),
1✔
1258
        }
1✔
1259
}
1✔
1260

1261
// NewBackendsWithConfig creates a new set of backends with the given config for all backends.
1262
// Useful for setting up client with a custom logger and http client.
1263
func NewBackendsWithConfig(config *BackendConfig) *Backends {
×
1264
        return &Backends{
×
1265
                API:     GetBackendWithConfig(APIBackend, config),
×
1266
                Connect: GetBackendWithConfig(ConnectBackend, config),
×
1267
                Uploads: GetBackendWithConfig(UploadsBackend, config),
×
1268
        }
×
1269
}
×
1270

1271
// ParseID attempts to parse a string scalar from a given JSON value which is
1272
// still encoded as []byte. If the value was a string, it returns the string
1273
// along with true as the second return value. If not, false is returned as the
1274
// second return value.
1275
//
1276
// The purpose of this function is to detect whether a given value in a
1277
// response from the Stripe API is a string ID or an expanded object.
1278
func ParseID(data []byte) (string, bool) {
93✔
1279
        s := string(data)
93✔
1280

93✔
1281
        if !strings.HasPrefix(s, "\"") {
151✔
1282
                return "", false
58✔
1283
        }
58✔
1284

1285
        if !strings.HasSuffix(s, "\"") {
35✔
1286
                return "", false
×
1287
        }
×
1288

1289
        // Edge case that should never happen; found via fuzzing
1290
        if s == "\"" {
36✔
1291
                return "", false
1✔
1292
        }
1✔
1293

1294
        return s[1 : len(s)-1], true
34✔
1295
}
1296

1297
// SetAppInfo sets app information. See AppInfo.
1298
func SetAppInfo(info *AppInfo) {
4✔
1299
        if info != nil && info.Name == "" {
4✔
1300
                panic(fmt.Errorf("App info name cannot be empty"))
×
1301
        }
1302
        appInfo = info
4✔
1303

4✔
1304
        // This is run in init, but we need to reinitialize it now that we have
4✔
1305
        // some app info.
4✔
1306
        initUserAgent()
4✔
1307
}
1308

1309
// SetBackend sets the backend used in the binding.
1310
func SetBackend(backend SupportedBackend, b Backend) {
2✔
1311
        backends.mu.Lock()
2✔
1312
        defer backends.mu.Unlock()
2✔
1313

2✔
1314
        switch backend {
2✔
1315
        case APIBackend:
1✔
1316
                backends.API = b
1✔
1317
        case ConnectBackend:
×
1318
                backends.Connect = b
×
1319
        case UploadsBackend:
1✔
1320
                backends.Uploads = b
1✔
1321
        }
1322
}
1323

1324
// SetHTTPClient overrides the default HTTP client.
1325
// This is useful if you're running in a Google AppEngine environment
1326
// where the http.DefaultClient is not available.
1327
func SetHTTPClient(client *http.Client) {
1✔
1328
        httpClient = client
1✔
1329
}
1✔
1330

1331
// String returns a pointer to the string value passed in.
1332
func String(v string) *string {
93✔
1333
        return &v
93✔
1334
}
93✔
1335

1336
// StringValue returns the value of the string pointer passed in or
1337
// "" if the pointer is nil.
1338
func StringValue(v *string) string {
22✔
1339
        if v != nil {
29✔
1340
                return *v
7✔
1341
        }
7✔
1342
        return ""
15✔
1343
}
1344

1345
// StringSlice returns a slice of string pointers given a slice of strings.
1346
func StringSlice(v []string) []*string {
2✔
1347
        out := make([]*string, len(v))
2✔
1348
        for i := range v {
5✔
1349
                out[i] = &v[i]
3✔
1350
        }
3✔
1351
        return out
2✔
1352
}
1353

1354
func AddBetaVersion(betaName string, betaVersion string) error {
2✔
1355
        if strings.Contains(APIVersion, "; "+betaName+"=") {
3✔
1356
                return fmt.Errorf("Stripe version header %s already contains entry for beta %s", APIVersion, betaName)
1✔
1357
        }
1✔
1358
        APIVersion = fmt.Sprintf("%s; %s=%s", APIVersion, betaName, betaVersion)
1✔
1359
        return nil
1✔
1360
}
1361

1362
//
1363
// Private constants
1364
//
1365

1366
// clientversion is the binding version
1367
const clientversion = "78.12.0-beta.1"
1368

1369
// defaultHTTPTimeout is the default timeout on the http.Client used by the library.
1370
// This is chosen to be consistent with the other Stripe language libraries and
1371
// to coordinate with other timeouts configured in the Stripe infrastructure.
1372
const defaultHTTPTimeout = 80 * time.Second
1373

1374
// maxNetworkRetriesDelay and minNetworkRetriesDelay defines sleep time in milliseconds between
1375
// tries to send HTTP request again after network failure.
1376
const maxNetworkRetriesDelay = 5000 * time.Millisecond
1377
const minNetworkRetriesDelay = 500 * time.Millisecond
1378

1379
// The number of requestMetric objects to buffer for client telemetry. When the
1380
// buffer is full, new requestMetrics are dropped.
1381
const telemetryBufferSize = 16
1382

1383
//
1384
// Private types
1385
//
1386

1387
// nopReadCloser's sole purpose is to give us a way to turn an `io.Reader` into
1388
// an `io.ReadCloser` by adding a no-op implementation of the `Closer`
1389
// interface. We need this because `http.Request`'s `Body` takes an
1390
// `io.ReadCloser` instead of a `io.Reader`.
1391
type nopReadCloser struct {
1392
        io.Reader
1393
}
1394

1395
func (nopReadCloser) Close() error { return nil }
48✔
1396

1397
// stripeClientUserAgent contains information about the current runtime which
1398
// is serialized and sent in the `X-Stripe-Client-User-Agent` as additional
1399
// debugging information.
1400
type stripeClientUserAgent struct {
1401
        Application     *AppInfo `json:"application"`
1402
        BindingsVersion string   `json:"bindings_version"`
1403
        Language        string   `json:"lang"`
1404
        LanguageVersion string   `json:"lang_version"`
1405
        Publisher       string   `json:"publisher"`
1406
        Uname           string   `json:"uname"`
1407
}
1408

1409
// requestMetrics contains the id and duration of the last request sent
1410
type requestMetrics struct {
1411
        RequestDurationMS *int     `json:"request_duration_ms"`
1412
        RequestID         string   `json:"request_id"`
1413
        Usage             []string `json:"usage"`
1414
}
1415

1416
// requestTelemetry contains the payload sent in the
1417
// `X-Stripe-Client-Telemetry` header when BackendConfig.EnableTelemetry = true.
1418
type requestTelemetry struct {
1419
        LastRequestMetrics requestMetrics `json:"last_request_metrics"`
1420
}
1421

1422
//
1423
// Private variables
1424
//
1425

1426
var appInfo *AppInfo
1427
var backends Backends
1428
var encodedStripeUserAgent string
1429
var encodedStripeUserAgentReady *sync.Once
1430
var encodedUserAgent string
1431

1432
// The default HTTP client used for communication with any of Stripe's
1433
// backends.
1434
//
1435
// Can be overridden with the function `SetHTTPClient` or by setting the
1436
// `HTTPClient` value when using `BackendConfig`.
1437
//
1438
// When adding something new here, see also `stripe_go115.go` where you'll want
1439
// to add it as well.
1440
var httpClient = &http.Client{
1441
        Timeout: defaultHTTPTimeout,
1442

1443
        // There is a bug in Go's HTTP/2 implementation that occasionally causes it
1444
        // to send an empty body when it receives a `GOAWAY` message from a server:
1445
        //
1446
        //     https://github.com/golang/go/issues/32441
1447
        //
1448
        // This is particularly problematic for this library because the empty body
1449
        // results in no parameters being sent, which usually results in a 400,
1450
        // which is a status code expressly not covered by retry logic.
1451
        //
1452
        // The bug seems to be somewhat tricky to fix and hasn't seen any traction
1453
        // lately, so for now we're mitigating by disabling HTTP/2 in stripe-go by
1454
        // default. Users who like to live dangerously can still re-enable it by
1455
        // specifying a custom HTTP client. When the bug above is fixed, we can
1456
        // turn it back on.
1457
        //
1458
        // The particular methodology here for disabling HTTP/2 is a little
1459
        // confusing at first glance, but is recommended by the `net/http`
1460
        // documentation ("Programs that must disable HTTP/2 can do so by setting
1461
        // Transport.TLSNextProto (for clients) ... to a non-nil, empty map.")
1462
        //
1463
        // Note that the test suite still uses HTTP/2 to run as it specifies its
1464
        // own HTTP client with it enabled. See `testing/testing.go`.
1465
        //
1466
        // (Written 2019/07/24.)
1467
        //
1468
        // UPDATE: With the release of Go 1.15, this bug has been fixed.
1469
        // As such, `stripe_go115.go` contains conditionally-compiled code that sets
1470
        // a different HTTP client as the default, since Go 1.15+ does not contain
1471
        // the aforementioned HTTP/2 bug.
1472
        Transport: &http.Transport{
1473
                TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper),
1474
        },
1475
}
1476

1477
//
1478
// Private functions
1479
//
1480

1481
// getUname tries to get a uname from the system, but not that hard. It tries
1482
// to execute `uname -a`, but swallows any errors in case that didn't work
1483
// (i.e. non-Unix non-Mac system or some other reason).
1484
func getUname() string {
5✔
1485
        path, err := exec.LookPath("uname")
5✔
1486
        if err != nil {
5✔
1487
                return UnknownPlatform
×
1488
        }
×
1489

1490
        cmd := exec.Command(path, "-a")
5✔
1491
        var out bytes.Buffer
5✔
1492
        cmd.Stderr = nil // goes to os.DevNull
5✔
1493
        cmd.Stdout = &out
5✔
1494
        err = cmd.Run()
5✔
1495
        if err != nil {
5✔
1496
                return UnknownPlatform
×
1497
        }
×
1498

1499
        return out.String()
5✔
1500
}
1501

1502
func init() {
1✔
1503
        initUserAgent()
1✔
1504
}
1✔
1505

1506
func initUserAgent() {
5✔
1507
        encodedUserAgent = "Stripe/v1 GoBindings/" + clientversion
5✔
1508
        if appInfo != nil {
7✔
1509
                encodedUserAgent += " " + appInfo.formatUserAgent()
2✔
1510
        }
2✔
1511
        encodedStripeUserAgentReady = &sync.Once{}
5✔
1512
}
1513

1514
func getEncodedStripeUserAgent() string {
68✔
1515
        encodedStripeUserAgentReady.Do(func() {
73✔
1516
                stripeUserAgent := &stripeClientUserAgent{
5✔
1517
                        Application:     appInfo,
5✔
1518
                        BindingsVersion: clientversion,
5✔
1519
                        Language:        "go",
5✔
1520
                        LanguageVersion: runtime.Version(),
5✔
1521
                        Publisher:       "stripe",
5✔
1522
                        Uname:           getUname(),
5✔
1523
                }
5✔
1524
                marshaled, err := json.Marshal(stripeUserAgent)
5✔
1525
                // Encoding this struct should never be a problem, so we're okay to panic
5✔
1526
                // in case it is for some reason.
5✔
1527
                if err != nil {
5✔
1528
                        panic(err)
×
1529
                }
1530
                encodedStripeUserAgent = string(marshaled)
5✔
1531
        })
1532
        return encodedStripeUserAgent
68✔
1533
}
1534

1535
func isHTTPWriteMethod(method string) bool {
17✔
1536
        return method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch || method == http.MethodDelete
17✔
1537
}
17✔
1538

1539
// newBackendImplementation returns a new Backend based off a given type and
1540
// fully initialized BackendConfig struct.
1541
//
1542
// The vast majority of the time you should be calling GetBackendWithConfig
1543
// instead of this function.
1544
func newBackendImplementation(backendType SupportedBackend, config *BackendConfig) Backend {
28✔
1545
        enableTelemetry := EnableTelemetry
28✔
1546
        if config.EnableTelemetry != nil {
36✔
1547
                enableTelemetry = *config.EnableTelemetry
8✔
1548
        }
8✔
1549

1550
        var requestMetricsBuffer chan requestMetrics
28✔
1551

28✔
1552
        // only allocate the requestMetrics buffer if client telemetry is enabled.
28✔
1553
        if enableTelemetry {
56✔
1554
                requestMetricsBuffer = make(chan requestMetrics, telemetryBufferSize)
28✔
1555
        }
28✔
1556

1557
        return &BackendImplementation{
28✔
1558
                HTTPClient:           config.HTTPClient,
28✔
1559
                LeveledLogger:        config.LeveledLogger,
28✔
1560
                MaxNetworkRetries:    *config.MaxNetworkRetries,
28✔
1561
                Type:                 backendType,
28✔
1562
                URL:                  *config.URL,
28✔
1563
                enableTelemetry:      enableTelemetry,
28✔
1564
                networkRetriesSleep:  true,
28✔
1565
                requestMetricsBuffer: requestMetricsBuffer,
28✔
1566
        }
28✔
1567
}
1568

1569
func normalizeURL(url string) string {
28✔
1570
        // All paths include a leading slash, so to keep logs pretty, trim a
28✔
1571
        // trailing slash on the URL.
28✔
1572
        url = strings.TrimSuffix(url, "/")
28✔
1573

28✔
1574
        // For a long time we had the `/v1` suffix as part of a configured URL
28✔
1575
        // rather than in the per-package URLs throughout the library. Continue
28✔
1576
        // to support this for the time being by stripping one that's been
28✔
1577
        // passed for better backwards compatibility.
28✔
1578
        url = strings.TrimSuffix(url, "/v1")
28✔
1579

28✔
1580
        return url
28✔
1581
}
28✔
1582

1583
func RawRequest(method, path string, content string, params *RawParams) (*APIResponse, error) {
×
1584
        if bi, ok := GetBackend(APIBackend).(RawRequestBackend); ok {
×
1585
                return bi.RawRequest(method, path, Key, content, params)
×
1586
        }
×
1587
        return nil, fmt.Errorf("Error: cannot call RawRequest if backends.API is initialized with a backend that doesn't implement RawRequestBackend")
×
1588
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc