Skip to content

Commit

Permalink
Implemented /v1/discount-redemptions APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
AchoArnold committed Mar 18, 2023
1 parent 3a93760 commit 6599d2b
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 26 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ import "github.com/NdoleStudio/lemonsqueezy-go"
- `GET /v1/subscriptions`: List all subscriptions
- `DELETE /v1/subscriptions/{id}`: Cancel an active subscription
- **Subscription Invoices**
- `GET /v1/subscription-invoice/:id`: Retrieve a subscription invoice
- `GET /v1/subscription-invoices/:id`: Retrieve a subscription invoice
- `GET /v1/subscription-invoices`: List all subscription invoices
- **Discount Redemptions**
- `GET /v1/discount-redemptions/:id`: Retrieve a discount redemption
- `GET /v1/discount-redemptions`: List all discount redemptions
- **Webhooks**
- `Verify`: Verify that webhook requests are coming from Lemon Squeezy

Expand Down
2 changes: 2 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Client struct {
Orders *OrdersService
OrderItems *OrderItemsService
SubscriptionInvoices *SubscriptionInvoicesService
DiscountRedemptions *DiscountRedemptionsService
}

// New creates and returns a new Client from a slice of Option.
Expand Down Expand Up @@ -61,6 +62,7 @@ func New(options ...Option) *Client {
client.Orders = (*OrdersService)(&client.common)
client.OrderItems = (*OrderItemsService)(&client.common)
client.SubscriptionInvoices = (*SubscriptionInvoicesService)(&client.common)
client.DiscountRedemptions = (*DiscountRedemptionsService)(&client.common)

return client
}
Expand Down
5 changes: 3 additions & 2 deletions contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ type ApiResponseSelfLink struct {

// ApiResponseListLink defines a link for list os resources
type ApiResponseListLink struct {
First string `json:"first"`
Last string `json:"last"`
First string `json:"first"`
Last string `json:"last"`
Next *string `json:"next"`
}

// ApiResponseListMeta defines the meta data for a list api response
Expand Down
28 changes: 28 additions & 0 deletions discount_redemption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package client

import "time"

// DiscountRedemptionAttributes is a record of a discount being applied to an order.
type DiscountRedemptionAttributes struct {
DiscountID int `json:"discount_id"`
OrderID int `json:"order_id"`
DiscountName string `json:"discount_name"`
DiscountCode string `json:"discount_code"`
DiscountAmount int `json:"discount_amount"`
DiscountAmountType string `json:"discount_amount_type"`
Amount int `json:"amount"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

// ApiResponseRelationshipsDiscountRedemption relationships of a subscription invoice
type ApiResponseRelationshipsDiscountRedemption struct {
Discount ApiResponseLinks `json:"discount"`
Order ApiResponseLinks `json:"order"`
}

// DiscountRedemptionApiResponse is the api response for one subscription invoice
type DiscountRedemptionApiResponse = ApiResponse[DiscountRedemptionAttributes, ApiResponseRelationshipsDiscountRedemption]

// DiscountRedemptionsApiResponse is the api response for a list of subscription invoices.
type DiscountRedemptionsApiResponse = ApiResponseList[DiscountRedemptionAttributes, ApiResponseRelationshipsDiscountRedemption]
44 changes: 44 additions & 0 deletions discount_redemption_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package client

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

// DiscountRedemptionsService is the API client for the `/v1/discount-redemptions` endpoint
type DiscountRedemptionsService service

// Get returns the discount redemption with the given ID.
//
// https://docs.lemonsqueezy.com/api/discount-redemptions#retrieve-a-discount-redemption
func (service *DiscountRedemptionsService) Get(ctx context.Context, invoiceID string) (*DiscountRedemptionApiResponse, *Response, error) {
response, err := service.client.do(ctx, http.MethodGet, "/v1/discount-redemptions/"+invoiceID)
if err != nil {
return nil, response, err
}

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

return redemption, response, nil
}

// List a paginated list of discount redemptions.
//
// https://docs.lemonsqueezy.com/api/discount-redemptions#list-all-discount-redemptions
func (service *DiscountRedemptionsService) List(ctx context.Context) (*DiscountRedemptionsApiResponse, *Response, error) {
response, err := service.client.do(ctx, http.MethodGet, "/v1/discount-redemptions")
if err != nil {
return nil, response, err
}

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

return redemptions, response, nil
}
95 changes: 95 additions & 0 deletions discount_redemption_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package client

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 TestDiscountRedemptionsService_Get(t *testing.T) {
// Setup
t.Parallel()

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

// Act
redemption, response, err := client.DiscountRedemptions.Get(context.Background(), "1")

// Assert
assert.Nil(t, err)

assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, stubs.DiscountRedemptionGetResponse(), *response.Body)
assert.Equal(t, "1", redemption.Data.ID)

// Teardown
server.Close()
}

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

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

// Act
_, response, err := client.DiscountRedemptions.Get(context.Background(), "1")

// Assert
assert.NotNil(t, err)

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

// Teardown
server.Close()
}

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

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

// Act
redemptions, response, err := client.DiscountRedemptions.List(context.Background())

// Assert
assert.Nil(t, err)

assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, stubs.DiscountRedemptionsListResponse(), *response.Body)
assert.Equal(t, 1, len(redemptions.Data))

// Teardown
server.Close()
}

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

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

// Act
_, response, err := client.DiscountRedemptions.List(context.Background())

// Assert
assert.NotNil(t, err)

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

// Teardown
server.Close()
}
107 changes: 107 additions & 0 deletions internal/stubs/discount_redemption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package stubs

// DiscountRedemptionGetResponse is a dummy response to the GET /v1/discount-redemption/:id endpoint
func DiscountRedemptionGetResponse() []byte {
return []byte(`
{
"jsonapi": {
"version": "1.0"
},
"links": {
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1"
},
"data": {
"type": "discount-redemptions",
"id": "1",
"attributes": {
"discount_id": 1,
"order_id": 1,
"discount_name": "10%",
"discount_code": "10PERC",
"discount_amount": 10,
"discount_amount_type": "percent",
"amount": 999,
"created_at": "2023-02-07T10:30:01.000000Z",
"updated_at": "2023-02-07T10:30:01.000000Z"
},
"relationships": {
"discount": {
"links": {
"related": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/discount",
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/relationships/discount"
}
},
"order": {
"links": {
"related": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/order",
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/relationships/order"
}
}
},
"links": {
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1"
}
}
}
`)
}

// DiscountRedemptionsListResponse is a dummy response to GET /v1/discount-redemption
func DiscountRedemptionsListResponse() []byte {
return []byte(`
{
"meta": {
"page": {
"currentPage": 1,
"from": 1,
"lastPage": 1,
"perPage": 10,
"to": 10,
"total": 10
}
},
"jsonapi": {
"version": "1.0"
},
"links": {
"first": "https://api.lemonsqueezy.com/v1/discount-redemptions?page%5Bnumber%5D=1&page%5Bsize%5D=10&sort=-createdAt",
"last": "https://api.lemonsqueezy.com/v1/discount-redemptions?page%5Bnumber%5D=1339&page%5Bsize%5D=10&sort=-createdAt",
"next": "https://api.lemonsqueezy.com/v1/discount-redemptions?page%5Bnumber%5D=2&page%5Bsize%5D=10&sort=-createdAt"
},
"data": [
{
"type": "discount-redemptions",
"id": "1",
"attributes": {
"discount_id": 1,
"order_id": 1,
"discount_name": "10%",
"discount_code": "10PERC",
"discount_amount": 10,
"discount_amount_type": "percent",
"amount": 999,
"created_at": "2023-02-07T10:30:01.000000Z",
"updated_at": "2023-02-07T10:30:01.000000Z"
},
"relationships": {
"discount": {
"links": {
"related": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/discount",
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/relationships/discount"
}
},
"order": {
"links": {
"related": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/order",
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1/relationships/order"
}
}
},
"links": {
"self": "https://api.lemonsqueezy.com/v1/discount-redemptions/1"
}
}
]
}
`)
}
46 changes: 23 additions & 23 deletions subscription_invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@ import "time"

// SubscriptionInvoiceAttributes is the invoice for a subscription
type SubscriptionInvoiceAttributes struct {
StoreID int `json:"store_id"`
SubscriptionID int `json:"subscription_id"`
BillingReason string `json:"billing_reason"`
CardBrand string `json:"card_brand"`
CardLastFour string `json:"card_last_four"`
Currency string `json:"currency"`
CurrencyRate string `json:"currency_rate"`
Subtotal int `json:"subtotal"`
DiscountTotal int `json:"discount_total"`
Tax int `json:"tax"`
Total int `json:"total"`
SubtotalUsd int `json:"subtotal_usd"`
DiscountTotalUsd int `json:"discount_total_usd"`
TaxUsd int `json:"tax_usd"`
TotalUsd int `json:"total_usd"`
Status string `json:"status"`
StatusFormatted string `json:"status_formatted"`
Refunded int `json:"refunded"`
RefundedAt interface{} `json:"refunded_at"`
SubtotalFormatted string `json:"subtotal_formatted"`
DiscountTotalFormatted string `json:"discount_total_formatted"`
TaxFormatted string `json:"tax_formatted"`
TotalFormatted string `json:"total_formatted"`
StoreID int `json:"store_id"`
SubscriptionID int `json:"subscription_id"`
BillingReason string `json:"billing_reason"`
CardBrand string `json:"card_brand"`
CardLastFour string `json:"card_last_four"`
Currency string `json:"currency"`
CurrencyRate string `json:"currency_rate"`
Subtotal int `json:"subtotal"`
DiscountTotal int `json:"discount_total"`
Tax int `json:"tax"`
Total int `json:"total"`
SubtotalUsd int `json:"subtotal_usd"`
DiscountTotalUsd int `json:"discount_total_usd"`
TaxUsd int `json:"tax_usd"`
TotalUsd int `json:"total_usd"`
Status string `json:"status"`
StatusFormatted string `json:"status_formatted"`
Refunded int `json:"refunded"`
RefundedAt *time.Time `json:"refunded_at"`
SubtotalFormatted string `json:"subtotal_formatted"`
DiscountTotalFormatted string `json:"discount_total_formatted"`
TaxFormatted string `json:"tax_formatted"`
TotalFormatted string `json:"total_formatted"`
Urls struct {
InvoiceURL string `json:"invoice_url"`
} `json:"urls"`
Expand Down

0 comments on commit 6599d2b

Please sign in to comment.