~evanj/cms

ref: 991b370ac6b6208e5f2fd7dcada177d52be58d9f cms/internal/s/stripe/stripe.go -rw-r--r-- 2.4 KiB
991b370aEvan M Jones Feat(Big): Added a few things here... 1 year, 2 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
package stripe

import (
	"git.sr.ht/~evanj/cms/internal/m/tier"
	"git.sr.ht/~evanj/cms/internal/m/user"
	"github.com/pkg/errors"
	lib "github.com/stripe/stripe-go/v71"
	"github.com/stripe/stripe-go/v71/checkout/session"
	"github.com/stripe/stripe-go/v71/customer"
)

type Stripe struct {
	sucesssURL, cancelURL string
	pk, sk                string
	db                    DBer
}

type DBer interface {
	UserGetFromToken(token string) (user.User, error)
	OrgGiveTier(user.User, tier.Tier) error
}

func New(sucesssURL, cancelURL, pk, sk string, db DBer) Stripe {
	// Stripe sucks.
	lib.Key = sk
	return Stripe{
		sucesssURL, cancelURL,
		pk, sk,
		db,
	}
}

func (s Stripe) StartCheckout(user user.User, t tier.Tier) (string, string, error) {
	customerParams := &lib.CustomerParams{
		Name:        lib.String(user.Name()),
		Description: lib.String(user.Token()),
	}

	c, err := customer.New(customerParams)
	if err != nil {
		return "", "", err
	}

	params := &lib.CheckoutSessionParams{
		Customer: lib.String(c.ID),
		PaymentMethodTypes: lib.StringSlice([]string{
			"card",
		}),
		LineItems: []*lib.CheckoutSessionLineItemParams{
			&lib.CheckoutSessionLineItemParams{
				Price:    lib.String(t.StripePriceID),
				Quantity: lib.Int64(1),
			},
		},
		Mode:       lib.String("subscription"),
		SuccessURL: lib.String(s.sucesssURL),
		CancelURL:  lib.String(s.cancelURL),
	}

	sess, err := session.New(params)
	if err != nil {
		return "", "", err
	}

	return sess.ID, s.pk, nil
}

func (s Stripe) CompleteCheckout(sessionID string) error {
	sess, err := session.Get(sessionID, &lib.CheckoutSessionParams{
		PaymentIntentData: &lib.CheckoutSessionPaymentIntentDataParams{},
	})
	if err != nil {
		return errors.Wrap(err, "transaction session not found")
	}

	c, err := customer.Get(sess.Customer.ID, nil)
	if err != nil {
		return errors.Wrap(err, "customer not found")
	}

	// NOTE: Only one subscription can exist at once per user/org.
	var (
		t   tier.Tier
		def tier.Tier
		ok  bool
	)
	for _, sub := range c.Subscriptions.Data {
		for _, li := range sub.Items.Data {
			t, ok = tier.ByStripePriceID(li.Price.ID)
			if !ok {
				return errors.New("mangled subscription item")
			}
		}
	}

	if t.Name == def.Name {
		return errors.New("subscription could not be found")
	}

	user, err := s.db.UserGetFromToken(c.Description)
	if err != nil {
		return errors.Wrap(err, "mangled user token")
	}

	return s.db.OrgGiveTier(user, t)
}