@@ -7,15 +7,16 @@ import (
77 "encoding/json"
88 "errors"
99 "fmt"
10+ "io"
1011 "net/http"
1112 "net/http/httputil"
1213 "net/url"
14+ "time"
1315)
1416
1517var ErrUnexpectedResponse = errors .New ("unexpected response" )
1618var ErrLicenseKeyNotFound = errors .New ("license key not found" )
1719var ErrLicenseKeyAlreadyActivated = errors .New ("license key already activated" )
18- var ErrLicenseKeyExpired = errors .New ("license key expired" )
1920
2021type KeygenResponseError struct {
2122 DumpedResponse []byte
@@ -112,9 +113,12 @@ type validateLicenseKeyOptions struct {
112113 Fingerprint string
113114}
114115
116+ // LicenseID is an API object.
115117type LicenseID struct {
116- ID string
117- IsActivated bool
118+ ID string `json:"-"`
119+ ExpireAt * time.Time `json:"expire_at"`
120+ IsActivated bool `json:"is_activated"`
121+ IsExpired bool `json:"is_expired"`
118122}
119123
120124// validateLicenseKey returns the following errors:
@@ -164,48 +168,73 @@ func validateLicenseKey(ctx context.Context, client *http.Client, opts validateL
164168 }()
165169
166170 if resp .StatusCode >= 200 && resp .StatusCode < 300 {
167- var respBody map [string ]any
168- err = json .NewDecoder (resp .Body ).Decode (& respBody )
169- if err != nil {
170- return
171- }
171+ return parseValidateLicenseKeyResponseBody (resp .Body )
172+ }
172173
173- meta := respBody ["meta" ].(map [string ]any )
174- meta_code := meta ["code" ].(string )
174+ err = ErrUnexpectedResponse
175+ return
176+ }
175177
176- if meta_code == "NOT_FOUND" {
177- err = ErrLicenseKeyNotFound
178- return
179- } else {
180- status := respBody ["data" ].(map [string ]any )["attributes" ].(map [string ]any )["status" ].(string )
181- if status == "EXPIRED" {
182- err = ErrLicenseKeyExpired
183- return
184- }
178+ func parseValidateLicenseKeyResponseBody (r io.Reader ) (licenseID * LicenseID , err error ) {
179+ var respBody map [string ]any
180+ err = json .NewDecoder (r ).Decode (& respBody )
181+ if err != nil {
182+ return
183+ }
185184
186- switch meta_code {
187- case "FINGERPRINT_SCOPE_MISMATCH" :
188- err = ErrLicenseKeyAlreadyActivated
189- return
190- case "EXPIRED" :
191- err = ErrLicenseKeyExpired
192- return
193- case "NO_MACHINE" , "VALID" :
194- data , ok := respBody ["data" ].(map [string ]any )
195- if ! ok || data == nil {
196- err = ErrUnexpectedResponse
197- return
198- }
199- licenseID = & LicenseID {
200- ID : data ["id" ].(string ),
201- IsActivated : meta_code == "VALID" ,
202- }
203- return
204- }
185+ meta := respBody ["meta" ].(map [string ]any )
186+ meta_code := meta ["code" ].(string )
187+
188+ if meta_code == "NOT_FOUND" {
189+ err = ErrLicenseKeyNotFound
190+ return
191+ }
192+
193+ if meta_code == "FINGERPRINT_SCOPE_MISMATCH" {
194+ err = ErrLicenseKeyAlreadyActivated
195+ return
196+ }
197+
198+ data , ok := respBody ["data" ].(map [string ]any )
199+ if ! ok || data == nil {
200+ err = ErrUnexpectedResponse
201+ return
202+ }
203+ attributes , ok := data ["attributes" ].(map [string ]any )
204+ if ! ok {
205+ err = ErrUnexpectedResponse
206+ return
207+ }
208+
209+ licenseID = & LicenseID {
210+ ID : data ["id" ].(string ),
211+ }
212+
213+ if expiry , ok := attributes ["expiry" ].(string ); ok {
214+ var expireAt time.Time
215+ expireAt , err = time .Parse (time .RFC3339 , expiry )
216+ if err != nil {
217+ err = errors .Join (err , ErrUnexpectedResponse )
218+ return
205219 }
220+ licenseID .ExpireAt = & expireAt
221+ }
222+
223+ switch meta_code {
224+ case "VALID" :
225+ licenseID .IsActivated = true
226+ licenseID .IsExpired = false
227+ case "EXPIRED" :
228+ licenseID .IsActivated = true
229+ licenseID .IsExpired = true
230+ case "NO_MACHINE" :
231+ licenseID .IsActivated = false
232+ licenseID .IsExpired = false
233+ default :
234+ err = ErrUnexpectedResponse
235+ return
206236 }
207237
208- err = ErrUnexpectedResponse
209238 return
210239}
211240
@@ -421,10 +450,10 @@ type ActivateLicenseOptions struct {
421450// - ErrUnexpectedResponse
422451// - ErrLicenseKeyNotFound
423452// - ErrLicenseKeyAlreadyActivated
424- func ActivateLicense (ctx context.Context , client * http.Client , opts ActivateLicenseOptions ) (err error ) {
453+ func ActivateLicense (ctx context.Context , client * http.Client , opts ActivateLicenseOptions ) (licenseID * LicenseID , err error ) {
425454 // We first try to validate the license key.
426455 // If the license is activated, we can return early.
427- licenseID , err : = validateLicenseKey (ctx , client , validateLicenseKeyOptions {
456+ licenseID , err = validateLicenseKey (ctx , client , validateLicenseKeyOptions {
428457 KeygenConfig : opts .KeygenConfig ,
429458 LicenseKey : opts .LicenseKey ,
430459 Fingerprint : opts .Fingerprint ,
@@ -475,20 +504,12 @@ type CheckLicenseOptions struct {
475504// - ErrUnexpectedResponse
476505// - ErrLicenseKeyNotFound
477506// - ErrLicenseKeyAlreadyActivated
478- func CheckLicense (ctx context.Context , client * http.Client , opts CheckLicenseOptions ) (err error ) {
479- licenseID , err : = validateLicenseKey (ctx , client , validateLicenseKeyOptions {
507+ func CheckLicense (ctx context.Context , client * http.Client , opts CheckLicenseOptions ) (licenseID * LicenseID , err error ) {
508+ licenseID , err = validateLicenseKey (ctx , client , validateLicenseKeyOptions {
480509 KeygenConfig : opts .KeygenConfig ,
481510 LicenseKey : opts .LicenseKey ,
482511 Fingerprint : opts .Fingerprint ,
483512 })
484- if err != nil {
485- return
486- }
487- // Activate the same fingerprint is idempotent.
488- if licenseID .IsActivated {
489- return
490- }
491-
492513 return
493514}
494515
0 commit comments