~samwhited/paddle

c8abffc53683631ce9b528be2aa2536c5f5b6bc9 — Sam Whited 2 years ago aa52cb2
paddle, coupons: move coupon code to new package
2 files changed, 33 insertions(+), 23 deletions(-)

R coupons.go => coupon/coupons.go
R discount.go => coupon/discount.go
R coupons.go => coupon/coupons.go +32 -22
@@ 1,4 1,5 @@
package paddle
// Package coupon provides the /product/*_coupon APIs.
package coupon

import (
	"bytes"


@@ 7,6 8,9 @@ import (
	"strconv"
	"strings"
	"time"

	"soquee.net/paddle"
	"soquee.net/paddle/internal"
)

const (


@@ 16,27 20,33 @@ const (
	deleteCouponsURL = "/product/delete_coupon"
)

// ListProducts requests a list of coupons available for the given product and
// returns an iterator that decodes them from the response body.
// Client is used to perform API operations on Coupons.
type Client struct {
	*paddle.Client
}

// List requests a list of coupons available for the given product and returns
// an iterator that decodes them from the response body.
//
// If an error is returned, it is decoded and returned immediately.
func (c *Client) ListCoupons(productID int64) (*Coupons, error) {
func (c Client) List(productID int64) (Iter, error) {
	v := url.Values{}
	v.Add("product_id", strconv.FormatInt(productID, 10))

	msg, err := c.Do(listCouponsURL, v)
	if err != nil {
		return nil, err
		return Iter{}, err
	}
	return &Coupons{
	return Iter{
		d: json.NewDecoder(bytes.NewReader(msg)),
	}, nil
}

// CreateCoupons creates a number of identical coupons.
// Create creates a number of identical coupons.
// If num is greater than 1, coupon.Code is ignored and the codes are
// randomized.
// The coupon.Product field is ignored.
func (c *Client) CreateCoupons(num int, prefix, group string, couponType CouponType, recurring bool, coupon *Coupon) ([]string, error) {
func (c Client) Create(num int, prefix, group string, couponType CouponType, recurring bool, coupon *Coupon) ([]string, error) {
	v := url.Values{}
	if num <= 1 {
		if coupon.Code != "" {


@@ 65,7 75,7 @@ func (c *Client) CreateCoupons(num int, prefix, group string, couponType CouponT
		v.Add("currency", coupon.Currency)
	}
	v.Add("allowed_uses", strconv.Itoa(coupon.AllowedUses))
	v.Add("expires", time.Time(coupon.Expires).Format(dateFmt))
	v.Add("expires", time.Time(coupon.Expires).Format(internal.DateFmt))
	// TODO: paddle claims that when creating this you should use "0" or "1"… WTF?
	// See: https://paddle.com/docs/api-generate-coupon/
	v.Add("recurring", strconv.FormatBool(coupon.Recurring))


@@ 85,23 95,23 @@ func (c *Client) CreateCoupons(num int, prefix, group string, couponType CouponT
	return codes, err
}

// UpdateCoupon updates properties of a single coupon.
// Update updates properties of a single coupon.
//
// Empty values will be ignored.
// If products is nil, it will be ignored, if it is an empty slice, all products
// will be removed.
func (c *Client) UpdateCoupon(code string, coupon *Coupon) error {
func (c Client) Update(code string, coupon *Coupon) error {
	return c.updateCoupons("coupon_code", code, coupon.Code, coupon)
}

// UpdateCouponGroup updates properties of an entire group of coupons.
// UpdateGroup updates properties of an entire group of coupons.
//
// See UpdateCoupon for more information.
func (c *Client) UpdateCouponGroup(group, newGroup string, coupon *Coupon) error {
func (c Client) UpdateGroup(group, newGroup string, coupon *Coupon) error {
	return c.updateCoupons("code", group, newGroup, coupon)
}

func (c *Client) updateCoupons(typ, code, newCode string, coupon *Coupon) error {
func (c Client) updateCoupons(typ, code, newCode string, coupon *Coupon) error {
	v := url.Values{}
	v.Add(typ, code)
	if newCode != "" {


@@ 119,7 129,7 @@ func (c *Client) updateCoupons(typ, code, newCode string, coupon *Coupon) error 
		v.Add("product_ids", sb.String())
	}
	if !time.Time(coupon.Expires).IsZero() {
		v.Add("expires", time.Time(coupon.Expires).Format(dateFmt))
		v.Add("expires", time.Time(coupon.Expires).Format(internal.DateFmt))
	}
	if coupon.AllowedUses > 0 {
		v.Add("allowed_uses", strconv.Itoa(coupon.AllowedUses))


@@ 136,7 146,7 @@ func (c *Client) updateCoupons(typ, code, newCode string, coupon *Coupon) error 
}

// DeleteCoupon deletes the given coupon from the given product.
func (c *Client) DeleteCoupon(product int64, code string) error {
func (c Client) DeleteCoupon(product int64, code string) error {
	v := url.Values{}
	v.Add("product_id", strconv.FormatInt(product, 64))
	v.Add("coupon_code", code)


@@ 145,19 155,19 @@ func (c *Client) DeleteCoupon(product int64, code string) error {
	return err
}

// Coupons provides a mechanism for lazily decoding and iterating over a list
// of coupons.
type Coupons struct {
// Iter provides a mechanism for lazily decoding and iterating over a list of
// coupons.
type Iter struct {
	d *json.Decoder
}

// Next returns true if there are more coupons to decode.
func (c Coupons) Next() bool {
func (c Iter) Next() bool {
	return c.d.More()
}

// Decode returns the next coupon.
func (c Coupons) Decode() (*Coupon, error) {
func (c Iter) Decode() (*Coupon, error) {
	coupon := &Coupon{}
	err := c.d.Decode(coupon)
	return coupon, err


@@ 174,7 184,7 @@ type Coupon struct {
	AllowedUses    int          `json:"allowed_uses"`
	TimesUsed      int          `json:"times_used"`
	Recurring      bool         `json:"is_recurring"`
	Expires        Time         `json:"expires"`
	Expires        paddle.Time  `json:"expires"`
	Product        []int64      `json:"-"`
	Minimum        float64      `json:"minimum_threshold"`
}

R discount.go => coupon/discount.go +1 -1
@@ 1,4 1,4 @@
package paddle
package coupon

// DiscountType is a type of discount that can be applied with a coupon.
type DiscountType string