Skip to content

Commit

Permalink
replaced api version check in webhook/client with isCompatibleApiVers…
Browse files Browse the repository at this point in the history
…ion, which will test if the release train of the webhook event matches the pinned version, or return false for any event api version that does not have a release train
  • Loading branch information
jar-stripe committed Oct 23, 2024
1 parent be11c5f commit d132c81
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
16 changes: 15 additions & 1 deletion webhook/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,20 @@ type signedHeader struct {
// Private functions
//

func isCompatibleAPIVersion(eventApiVersion string) bool {
// If the event api version is from before we started adding
// a release train, there's no way its compatible with this
// version
if !strings.Contains(eventApiVersion, ".") {
return false
}

// versions are yyyy-MM-dd.train
var eventReleaseTrain = strings.Split(eventApiVersion, ".")[1]
var currentReleaseTrain = strings.Split(stripe.APIVersion, ".")[1]
return eventReleaseTrain == currentReleaseTrain
}

func constructEvent(payload []byte, sigHeader string, secret string, options ConstructEventOptions) (stripe.Event, error) {
e := stripe.Event{}

Expand All @@ -203,7 +217,7 @@ func constructEvent(payload []byte, sigHeader string, secret string, options Con
return e, fmt.Errorf("Failed to parse webhook body json: %s", err.Error())
}

if !options.IgnoreAPIVersionMismatch && e.APIVersion != stripe.APIVersion {
if !options.IgnoreAPIVersionMismatch && !isCompatibleAPIVersion(e.APIVersion) {
return e, fmt.Errorf("Received event with API version %s, but stripe-go %s expects API version %s. We recommend that you create a WebhookEndpoint with this API version. Otherwise, you can disable this error by using `ConstructEventWithOptions(..., ConstructEventOptions{..., ignoreAPIVersionMismatch: true})` but be wary that objects may be incorrectly deserialized.", e.APIVersion, stripe.ClientVersion, stripe.APIVersion)
}

Expand Down
63 changes: 62 additions & 1 deletion webhook/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,25 @@ var testPayload = []byte(fmt.Sprintf(`{
"object": "event",
"api_version": "%s"
}`, stripe.APIVersion))

var testPayloadWithNewVersionInReleaseTrain = []byte(fmt.Sprintf(`{
"id": "evt_test_webhook",
"object": "event",
"api_version": "%s"
}`, "2099-10-10."+strings.Split(stripe.APIVersion, ".")[1]))

var testPayloadWithAPIVersionMismatch = []byte(`{
"id": "evt_test_webhook",
"object": "event",
"api_version": "2020-01-01"
}`)

var testPayloadWithReleaseTrainVersionMismatch = []byte(`{
"id": "evt_test_webhook",
"object": "event",
"api_version": "2099-10-10.the_larch"
}`)

var testSecret = "whsec_test_secret"

func newSignedPayload(options ...func(*SignedPayload)) *SignedPayload {
Expand Down Expand Up @@ -179,7 +193,39 @@ func TestTokenNew(t *testing.T) {
}
}

func TestConstructEvent_ErrorOnAPIVersionMismatch(t *testing.T) {
func TestConstructEvent_SuccessOnExpectedAPIVersion(t *testing.T) {
p := newSignedPayload(func(p *SignedPayload) {
p.Payload = testPayload
})

evt, err := ConstructEvent(p.Payload, p.Header, p.Secret)

if err != nil {
t.Errorf("Unexpected error in ConstructEvent: %v", err)
}

if evt.APIVersion != stripe.APIVersion {
t.Errorf("Expected API versions to match")
}
}

func TestConstructEvent_SuccessOnNewAPIVersionInExpectedReleaseTrain(t *testing.T) {
p := newSignedPayload(func(p *SignedPayload) {
p.Payload = testPayloadWithNewVersionInReleaseTrain
})

evt, err := ConstructEvent(p.Payload, p.Header, p.Secret)

if err != nil {
t.Errorf("Unexpected error in ConstructEvent: %v", err)
}

expectedSuffix := "." + strings.Split(stripe.APIVersion, ".")[1]
if !strings.HasSuffix(evt.APIVersion, expectedSuffix) {
t.Errorf("Expected API release trains to match")
}
}
func TestConstructEvent_ErrorOnLegacyAPIVersionMismatch(t *testing.T) {
p := newSignedPayload(func(p *SignedPayload) {
p.Payload = testPayloadWithAPIVersionMismatch
})
Expand All @@ -195,6 +241,21 @@ func TestConstructEvent_ErrorOnAPIVersionMismatch(t *testing.T) {
}
}

func TestConstructEvent_ErrorOnReleaseTrainMismatch(t *testing.T) {
p := newSignedPayload(func(p *SignedPayload) {
p.Payload = testPayloadWithReleaseTrainVersionMismatch
})

_, err := ConstructEvent(p.Payload, p.Header, p.Secret)

if err == nil {
t.Errorf("Expected error due to API version mismatch.")
}

if !strings.Contains(err.Error(), "Received event with API version") {
t.Errorf("Expected API version mismatch error but received %v", err)
}
}
func TestConstructEventWithOptions_IgnoreAPIVersionMismatch(t *testing.T) {

p := newSignedPayload(func(p *SignedPayload) {
Expand Down

0 comments on commit d132c81

Please sign in to comment.