Skip to content

Commit

Permalink
Merge pull request #13 from igolaizola/licenses-endpoint
Browse files Browse the repository at this point in the history
Add license activate, validate and deactivate
  • Loading branch information
AchoArnold authored Jan 13, 2024
2 parents d66aeba + a507773 commit e43a382
Show file tree
Hide file tree
Showing 5 changed files with 373 additions and 0 deletions.
2 changes: 2 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Client struct {
Checkouts *CheckoutsService
LicenseKeys *LicenseKeysService
LicenseKeyInstances *LicenseKeyInstancesService
Licenses *LicensesService
}

// New creates and returns a new Client from a slice of Option.
Expand Down Expand Up @@ -73,6 +74,7 @@ func New(options ...Option) *Client {
client.Checkouts = (*CheckoutsService)(&client.common)
client.LicenseKeys = (*LicenseKeysService)(&client.common)
client.LicenseKeyInstances = (*LicenseKeyInstancesService)(&client.common)
client.Licenses = (*LicensesService)(&client.common)

return client
}
Expand Down
104 changes: 104 additions & 0 deletions internal/stubs/licenses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package stubs

// LicenseActivateResponse is a dummy response to the POST /v1/licenses/activate endpoint
func LicenseActivateResponse() []byte {
return []byte(`
{
"activated": true,
"error": null,
"license_key": {
"id": 1,
"status": "active",
"key": "38b1460a-5104-4067-a91d-77b872934d51",
"activation_limit": 1,
"activation_usage": 5,
"created_at": "2021-01-24T14:15:07.000000Z",
"expires_at": null
},
"instance": {
"id": "47596ad9-a811-4ebf-ac8a-03fc7b6d2a17",
"name": "Test",
"created_at": "2021-04-06T14:15:07.000000Z"
},
"meta": {
"store_id": 1,
"order_id": 2,
"order_item_id": 3,
"product_id": 4,
"product_name": "Example Product",
"variant_id": 5,
"variant_name": "Default",
"customer_id": 6,
"customer_name": "Luke Skywalker",
"customer_email": "[email protected]"
}
}
`)
}

// LicenseValidateResponse is a dummy response to the POST /v1/licenses/validate endpoint
func LicenseValidateResponse() []byte {
return []byte(`
{
"valid": true,
"error": null,
"license_key": {
"id": 1,
"status": "active",
"key": "38b1460a-5104-4067-a91d-77b872934d51",
"activation_limit": 1,
"activation_usage": 5,
"created_at": "2021-01-24T14:15:07.000000Z",
"expires_at": "2022-01-24T14:15:07.000000Z"
},
"instance": {
"id": "f90ec370-fd83-46a5-8bbd-44a241e78665",
"name": "Test",
"created_at": "2021-02-24T14:15:07.000000Z"
},
"meta": {
"store_id": 1,
"order_id": 2,
"order_item_id": 3,
"product_id": 4,
"product_name": "Example Product",
"variant_id": 5,
"variant_name": "Default",
"customer_id": 6,
"customer_name": "Luke Skywalker",
"customer_email": "[email protected]"
}
}
`)
}

// LicenseDeactivateResponse is a dummy response to the POST /v1/licenses/deactivate endpoint
func LicenseDeactivateResponse() []byte {
return []byte(`
{
"deactivated": true,
"error": null,
"license_key": {
"id": 1,
"status": "inactive",
"key": "38b1460a-5104-4067-a91d-77b872934d51",
"activation_limit": 5,
"activation_usage": 0,
"created_at": "2021-01-24T14:15:07.000000Z",
"expires_at": null
},
"meta": {
"store_id": 1,
"order_id": 2,
"order_item_id": 3,
"product_id": 4,
"product_name": "Example Product",
"variant_id": 5,
"variant_name": "Default",
"customer_id": 6,
"customer_name": "Luke Skywalker",
"customer_email": "[email protected]"
}
}
`)
}
54 changes: 54 additions & 0 deletions licenses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package lemonsqueezy

import "time"

type LicenseAttributes struct {
Error string `json:"error"`
LicenseKey LicenseKey `json:"license_key"`
Instance LicenseInstance `json:"instance"`
Meta LicenseMeta `json:"meta"`
}

type LicenseKey struct {
ID int `json:"id"`
Status string `json:"status"`
Key string `json:"key"`
ActivationLimit int `json:"activation_limit"`
ActivationUsage int `json:"activation_usage"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt *time.Time `json:"expires_at"`
TestMode bool `json:"test_mode"`
}

type LicenseInstance struct {
ID string `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
}

type LicenseMeta struct {
StoreID int `json:"store_id"`
OrderID int `json:"order_id"`
OrderItemID int `json:"order_item_id"`
VariantID int `json:"variant_id"`
VariantName string `json:"variant_name"`
ProductID int `json:"product_id"`
ProductName string `json:"product_name"`
CustomerID int `json:"customer_id"`
CustomerName string `json:"customer_name"`
CustomerEmail string `json:"customer_email"`
}

type LicenseActivateApiResponse struct {
Activated bool `json:"activated"`
LicenseAttributes
}

type LicenseValidateApiResponse struct {
Valid bool `json:"valid"`
LicenseAttributes
}
type LicenseDeactivateApiResponse struct {
Deactivated bool `json:"deactivated"`
LicenseAttributes
}
76 changes: 76 additions & 0 deletions licenses_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package lemonsqueezy

import (
"context"
"encoding/json"
"net/http"
)

// LicensesService is the API client for the `/v1/licenses` endpoint
type LicensesService service

// Activate a license key.
//
// https://docs.lemonsqueezy.com/help/licensing/license-api
func (service *LicensesService) Activate(ctx context.Context, licenseKey, instanceName string) (*LicenseActivateApiResponse, *Response, error) {
payload := map[string]any{
"license_key": licenseKey,
"instance_name": instanceName,
}

response, err := service.client.do(ctx, http.MethodPost, "/v1/licenses/activate", payload)
if err != nil {
return nil, response, err
}

activation := new(LicenseActivateApiResponse)
if err = json.Unmarshal(*response.Body, activation); err != nil {
return nil, response, err
}

return activation, response, nil
}

// Validate a license key.
//
// https://docs.lemonsqueezy.com/help/licensing/license-api
func (service *LicensesService) Validate(ctx context.Context, licenseKey, instanceID string) (*LicenseValidateApiResponse, *Response, error) {
payload := map[string]any{
"license_key": licenseKey,
"instance_id": instanceID,
}

response, err := service.client.do(ctx, http.MethodPost, "/v1/licenses/validate", payload)
if err != nil {
return nil, response, err
}

validation := new(LicenseValidateApiResponse)
if err = json.Unmarshal(*response.Body, validation); err != nil {
return nil, response, err
}

return validation, response, nil
}

// Deactivate a license key.
//
// https://docs.lemonsqueezy.com/help/licensing/license-api
func (service *LicensesService) Deactivate(ctx context.Context, licenseKey, instanceID string) (*LicenseDeactivateApiResponse, *Response, error) {
payload := map[string]any{
"license_key": licenseKey,
"instance_id": instanceID,
}

response, err := service.client.do(ctx, http.MethodPost, "/v1/licenses/deactivate", payload)
if err != nil {
return nil, response, err
}

deactivation := new(LicenseDeactivateApiResponse)
if err = json.Unmarshal(*response.Body, deactivation); err != nil {
return nil, response, err
}

return deactivation, response, nil
}
137 changes: 137 additions & 0 deletions licenses_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package lemonsqueezy

import (
"context"
"net/http"
"testing"

"github.com/NdoleStudio/lemonsqueezy-go/internal/helpers"
"github.com/NdoleStudio/lemonsqueezy-go/internal/stubs"
"github.com/stretchr/testify/assert"
)

func TestLicensesService_Activate(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusOK, stubs.LicenseActivateResponse())
client := New(WithBaseURL(server.URL))

// Act
licenseActivation, response, err := client.Licenses.Activate(context.Background(), "1234567890", "test")

// Assert
assert.Nil(t, err)

assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, stubs.LicenseActivateResponse(), *response.Body)
assert.Equal(t, true, licenseActivation.Activated)

// Teardown
server.Close()
}

func TestLicensesService_ActivateWithError(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusInternalServerError, nil)
client := New(WithBaseURL(server.URL))

// Act
_, response, err := client.Licenses.Activate(context.Background(), "1234567890", "test")

// Assert
assert.NotNil(t, err)

assert.Equal(t, http.StatusInternalServerError, response.HTTPResponse.StatusCode)

// Teardown
server.Close()
}

func TestLicensesService_Validate(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusOK, stubs.LicenseValidateResponse())
client := New(WithBaseURL(server.URL))

// Act
licenseValidation, response, err := client.Licenses.Validate(context.Background(), "1234567890", "")

// Assert
assert.Nil(t, err)

assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, stubs.LicenseValidateResponse(), *response.Body)
assert.Equal(t, true, licenseValidation.Valid)

// Teardown
server.Close()
}

func TestLicensesService_ValidateWithError(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusInternalServerError, nil)
client := New(WithBaseURL(server.URL))

// Act
_, response, err := client.Licenses.Validate(context.Background(), "1234567890", "")

// Assert
assert.NotNil(t, err)

assert.Equal(t, http.StatusInternalServerError, response.HTTPResponse.StatusCode)

// Teardown
server.Close()
}

func TestLicensesService_Deactivate(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusOK, stubs.LicenseDeactivateResponse())
client := New(WithBaseURL(server.URL))

// Act
licenseDeactivation, response, err := client.Licenses.Deactivate(context.Background(), "1234567890", "abc123")

// Assert
assert.Nil(t, err)

assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, stubs.LicenseDeactivateResponse(), *response.Body)
assert.Equal(t, true, licenseDeactivation.Deactivated)

// Teardown
server.Close()
}

func TestLicensesService_DeactivateWithError(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusInternalServerError, nil)
client := New(WithBaseURL(server.URL))

// Act
_, response, err := client.Licenses.Deactivate(context.Background(), "1234567890", "abc123")

// Assert
assert.NotNil(t, err)

assert.Equal(t, http.StatusInternalServerError, response.HTTPResponse.StatusCode)

// Teardown
server.Close()
}

0 comments on commit e43a382

Please sign in to comment.