~samwhited/paddle

ref: v0.0.3 paddle/plan/plans.go -rw-r--r-- 3.1 KiB
fa35a6a5Sam Whited paddle: add note about maintenance status 1 year, 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Package plan provides APIs related to plans.
package plan

import (
	"bytes"
	"encoding/json"
	"net/url"
	"strconv"
	"strings"

	"code.soquee.net/paddle"
)

const (
	listPlansURL   = "/subscription/plans"
	createPlansURL = "/subscription/plans_create"
)

// Client performs API operations.
type Client struct {
	*paddle.Client
}

// Get returns a single plan by its ID.
func (c Client) Get(plan uint64) (*Plan, error) {
	iter, err := c.list(int64(plan))
	if err != nil {
		return nil, err
	}
	return iter.Decode()
}

// List returns an iterator over all of the existing plans.
func (c Client) List() (Iter, error) {
	return c.list(-1)
}

func (c Client) list(plan int64) (Iter, error) {
	v := url.Values{}
	if plan >= 0 {
		v.Add("plan", strconv.FormatInt(plan, 10))
	}
	msg, err := c.Do(listPlansURL, v)
	if err != nil {
		return Iter{}, err
	}
	return Iter{
		d: json.NewDecoder(bytes.NewReader(msg)),
	}, nil
}

// Create makes a new plan.
func (c Client) Create(plan *Plan) (id uint64, err error) {
	v := url.Values{}
	v.Add("plan_type", string(plan.BillingType))
	v.Add("plan_length", strconv.FormatUint(uint64(plan.BillingPeriod), 10))
	v.Add("plan_trial_days", strconv.FormatUint(uint64(plan.TrialDays), 10))

	currency := string(plan.MainCurrency)
	v.Add("main_currency_code", currency)

	currency = strings.ToLower(currency)
	v.Add("recurring_price_"+strings.ToLower(currency), strconv.FormatFloat(plan.RecurringPrice[plan.MainCurrency], 'f', -1, 64))

	// TODO: is this even a thing? The main_currency_code docs mention it, but the parameters list doesn't contain it.
	v.Add("initial_price_"+strings.ToLower(currency), strconv.FormatFloat(plan.InitialPrice[plan.MainCurrency], 'f', -1, 64))

	msg, err := c.Do(createPlansURL, v)
	if err != nil {
		return 0, err
	}
	planResp := struct {
		ID uint64 `json:"product_id"`
	}{}
	err = json.Unmarshal(msg, &planResp)
	return planResp.ID, err
}

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

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

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

// BillingType represents a frequency at which the plan can be billed.
type BillingType string

// A list of acceptable billing types.
const (
	BillingDaily   = "day"
	BillingWeekly  = "week"
	BillingMonthly = "month"
	BillingYearly  = "year"
)

// Plan represents a subscription plan.
type Plan struct {
	ID             uint64                      `json:"id"`
	Name           string                      `json:"name"`
	BillingType    BillingType                 `json:"billing_type"`
	BillingPeriod  uint                        `json:"billing_period"`
	InitialPrice   map[paddle.Currency]float64 `json:"initial_price"`
	RecurringPrice map[paddle.Currency]float64 `json:"recurring_price"`
	TrialDays      uint                        `json:"trial_days"`

	// Used to pick an InitialPrice and RecurringPrice when creating a plan.
	// Not part of the plan list response.
	MainCurrency paddle.Currency `json:"-"`
}