Skip to content

Commit

Permalink
Add Stripe integration.
Browse files Browse the repository at this point in the history
  • Loading branch information
v0ctor committed Nov 26, 2023
1 parent 10047b3 commit 9f7bdf4
Show file tree
Hide file tree
Showing 38 changed files with 1,651 additions and 70 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ FRONT_PASSWORD_AUTHORIZATION_PATH=recover

ORG_NAME='Associació Valenciana pel Transport Públic'
ORG_LOGO=https://cdn.avptp.org/brand/imagotype.png

STRIPE_API_SECRET=
STRIPE_ENDPOINT_SECRET=
STRIPE_PRICE_ID=price_1O69HOIe11Eg2JMUZNFN0xhc
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
github.com/sarulabs/dingo/v4 v4.2.0
github.com/stoewer/go-strcase v1.3.0
github.com/stretchr/testify v1.8.4
github.com/stripe/stripe-go/v76 v76.1.0
github.com/vektah/gqlparser/v2 v2.5.10
golang.org/x/net v0.17.0
golang.org/x/text v0.13.0
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,14 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v76 v76.1.0 h1:TvpIuqJRBw2DmTecXz1DRq3KXChxy4YJ2iUu0r6c7uY=
github.com/stripe/stripe-go/v76 v76.1.0/go.mod h1:rw1MxjlAKKcZ+3FOXgTHgwiOa2ya6CPq6ykpJ0Q6Po4=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 h1:L0rPdfzq43+NV8rfIx2kA4iSSLRj2jN5ijYHoeXRwvQ=
Expand Down Expand Up @@ -207,6 +210,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
Expand All @@ -223,6 +227,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -239,6 +244,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
Expand Down
74 changes: 74 additions & 0 deletions internal/api/resolvers/billing.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions internal/api/resolvers/person.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/api/resolvers/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package resolvers

import (
"github.com/avptp/brain/internal/auth"
"github.com/avptp/brain/internal/billing"
"github.com/avptp/brain/internal/config"
"github.com/avptp/brain/internal/generated/data"
"github.com/avptp/brain/internal/messaging"
"github.com/go-redis/redis_rate/v10"
)

type Resolver struct {
biller billing.Biller
captcha auth.Captcha
cfg *config.Config
data *data.Client
Expand All @@ -17,13 +19,15 @@ type Resolver struct {
}

func NewResolver(
biller billing.Biller,
captcha auth.Captcha,
cfg *config.Config,
data *data.Client,
limiter *redis_rate.Limiter,
messenger messaging.Messenger,
) *Resolver {
return &Resolver{
biller,
captcha,
cfg,
data,
Expand Down
126 changes: 126 additions & 0 deletions internal/api/resolvers/resolvers_test/billing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package resolvers_test

import (
"github.com/avptp/brain/internal/api/reporting"
"github.com/avptp/brain/internal/generated/data"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/mock"
)

func (t *TestSuite) TestBilling() {
const createCheckoutSessionMutation = `
mutation() {
createBillingCheckoutSession() {
checkoutSessionId
}
}
`

type createCheckoutSession struct {
CreateBillingCheckoutSession struct {
CheckoutSessionID string
}
}

t.Run("create_checkout_session", func() {
authenticated, _, _, _, _ := t.authenticate()

id := gofakeit.LetterN(16)

t.biller.On(
"PreparePerson",
mock.Anything,
mock.IsType(&data.Client{}),
mock.IsType(&data.Person{}),
).Return(nil).Once()

t.biller.On(
"CreateCheckoutSession",
mock.IsType(&data.Person{}),
).Return(
id,
nil,
).Once()

var response createCheckoutSession
err := t.api.Post(
createCheckoutSessionMutation,
&response,
authenticated,
)

t.biller.AssertExpectations(t.T())

t.NoError(err)
t.Equal(id, response.CreateBillingCheckoutSession.CheckoutSessionID)
})

t.Run("create_checkout_session_without_authentication", func() {
var response createCheckoutSession
err := t.api.Post(
createCheckoutSessionMutation,
&response,
)

t.NotNil(err)
t.ErrorContains(err, reporting.ErrUnauthenticated.Message)
})

const createPortalSessionMutation = `
mutation() {
createBillingPortalSession() {
portalSessionId
}
}
`

type createPortalSession struct {
CreateBillingPortalSession struct {
PortalSessionID string
}
}

t.Run("create_portal_session", func() {
authenticated, _, _, _, _ := t.authenticate()

id := gofakeit.LetterN(16)

t.biller.On(
"PreparePerson",
mock.Anything,
mock.IsType(&data.Client{}),
mock.IsType(&data.Person{}),
).Return(nil).Once()

t.biller.On(
"CreatePortalSession",
mock.IsType(&data.Person{}),
).Return(
id,
nil,
).Once()

var response createPortalSession
err := t.api.Post(
createPortalSessionMutation,
&response,
authenticated,
)

t.biller.AssertExpectations(t.T())

t.NoError(err)
t.Equal(id, response.CreateBillingPortalSession.PortalSessionID)
})

t.Run("create_portal_session_without_authentication", func() {
var response createPortalSession
err := t.api.Post(
createPortalSessionMutation,
&response,
)

t.NotNil(err)
t.ErrorContains(err, reporting.ErrUnauthenticated.Message)
})
}
9 changes: 9 additions & 0 deletions internal/api/resolvers/resolvers_test/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/avptp/brain/internal/auth/auth_test"
"github.com/avptp/brain/internal/billing/billing_test"
"github.com/avptp/brain/internal/config"
"github.com/avptp/brain/internal/generated/container"
"github.com/avptp/brain/internal/generated/data"
Expand Down Expand Up @@ -55,6 +56,7 @@ type TestSuite struct {
suite.Suite

ctn *container.Container
biller *billing_test.MockedBiller
captcha *auth_test.MockedCaptcha
cfg *config.Config
data *data.Client
Expand All @@ -73,6 +75,13 @@ func (t *TestSuite) SetupSuite() {
panic(err) // unrecoverable situation
}

t.biller = &billing_test.MockedBiller{}
err = builder.Set(services.Biller, t.biller)

if err != nil {
panic(err) // unrecoverable situation
}

t.captcha = &auth_test.MockedCaptcha{}
err = builder.Set(services.Captcha, t.captcha)

Expand Down
7 changes: 7 additions & 0 deletions internal/api/resolvers/resolvers_test/person_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ func (t *TestSuite) TestPerson() {

input := t.factory.Person().Fields

t.biller.On(
"SyncPerson",
mock.IsType(&data.Person{}),
).Return(nil).Once()

var response update
err := t.api.Post(
updateMutation,
Expand All @@ -271,6 +276,8 @@ func (t *TestSuite) TestPerson() {
client.Var("country", input.Country),
)

t.biller.AssertExpectations(t.T())

t.NoError(err)
t.Equal(p.ID, t.toUUID(response.UpdatePerson.Person.ID))

Expand Down
Loading

0 comments on commit 9f7bdf4

Please sign in to comment.