Skip to content

Commit

Permalink
adding subscription functionalities (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
ddelizia committed Sep 19, 2021
1 parent 89448fb commit 7764073
Show file tree
Hide file tree
Showing 50 changed files with 816 additions and 191 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: ci
name: Continuous Integration
on:
push:
branches:
Expand All @@ -10,7 +10,8 @@ on:

env:
STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
STRIPE_PRICE_ID: ${{ secrets.STRIPE_PRICE_ID }}
STRIPE_BASIC_PRICE_ID: ${{ secrets.STRIPE_BASIC_PRICE_ID }}
STRIPE_PREMIUM_PRICE_ID: ${{ secrets.STRIPE_PREMIUM_PRICE_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}

HASURA_GRAPHQL_ADMIN_SECRET: "mysecrethasura"
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: ci
name: Release Build
on:
push:
tags:
Expand All @@ -10,7 +10,7 @@ env:
REGISTRY: ghcr.io

jobs:

docker:
name: Create docker image and publish
runs-on: ubuntu-latest
Expand All @@ -27,6 +27,17 @@ jobs:

- name: Log docker images
run: ./scripts/docker-push.sh $GITHUB_SHA ${GITHUB_REF#refs/tags/}

- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: true

go-list:
name: Create go package
Expand Down
12 changes: 9 additions & 3 deletions cmd/stripe_event_code_generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ const PATH string = "pkg/subscription"
const TEMPLATE_FILE string = `package subscription
import (
"context"
"github.com/ddelizia/hasura-saas/pkg/gqlsdk"
"github.com/stripe/stripe-go"
)
func Process{{ .EventName }}(event stripe.Event, id string) error {
func Process{{ .EventName }}(c context.Context, event stripe.Event, id string, sdkSvc gqlsdk.Service) error {
data := &stripe.{{ .TypeName }}{}
if err := beforeEvent(event, data); err != nil {
return err
Expand All @@ -46,18 +49,21 @@ func Process{{ .EventName }}(event stripe.Event, id string) error {
const TEMPLATE_MAPPING string = `package subscription
import (
"context"
"github.com/ddelizia/hasura-saas/pkg/gqlsdk"
"github.com/sirupsen/logrus"
"github.com/stripe/stripe-go"
)
// Autogenerated by ./cmd/stripe_event_code_generator/main.go
func EventMapping(event stripe.Event, id string) {
func EventMapping(c context.Context, event stripe.Event, id string, sdkSvc gqlsdk.Service) {
switch event.Type {
{{ range $key, $value := . }}
case "{{ $key }}":
Process{{ $value }}(event, id)
Process{{ $value }}(c, event, id, sdkSvc)
{{- end }}
default:
Expand Down
10 changes: 8 additions & 2 deletions cmd/subscription/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ func main() {
hshttp.MiddlewareSetContentTypeApplicationJson,
)).Methods("POST")

// TODO not ready yet
handlerRetry := subscription.NewRetryHandler(graphqlSevice, sdkService)
r.Handle("/retry",
hshttp.MiddlewareChain(
Expand All @@ -59,7 +58,14 @@ func main() {
hshttp.MiddlewareSetContentTypeApplicationJson,
)).Methods("POST")

// TODO not ready yet
handlerChange := subscription.NewChangeHandler(graphqlSevice, sdkService)
r.Handle("/change",
hshttp.MiddlewareChain(
handlerChange.ServeHTTP,
hshttp.MiddlewareLogRequest,
hshttp.MiddlewareSetContentTypeApplicationJson,
)).Methods("POST")

handlerWebhook := subscription.NewWebhookHandler(sdkService)
r.Handle("/webhook", handlerWebhook).Methods("POST")

Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ services:
- ./cmd/:/app/cmd/:ro
- ./pkg/:/app/pkg/:ro
environment:
SUBSCRIPTION.STRIPE.APIKEY: ${STRIPE_KEY}
SUBSCRIPTION.STRIPE.WEBHOOKSECRET: ${STRIPE_WEBHOOK_SECRET}
GRAPHQL.HASURA.ADMINSECRET: ${HASURA_GRAPHQL_ADMIN_SECRET}
GRAPHQL.URL: "http://graphql-engine:8080/v1/graphql"
LOGGER.LEVEL: "debug"
Expand Down
12 changes: 10 additions & 2 deletions graphql/mutation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,19 @@ mutation CreateSubscriptionCustomer($name: String!, $id_plan: String!, $id_user:
}

## Subscription
mutation SetSubscriptioStatus($status: String!, $is_active: Boolean!, $accountId: uuid!, $stripe_subscription_id: String!) {
update_subscription_status(where: {id_account: {_eq: $accountId}}, _set: {status: $status, is_active: $is_active, stripe_subscription_id: $stripe_subscription_id}) {
mutation SetSubscriptioStatus($status: String!, $is_active: Boolean!, $accountId: uuid!, $stripe_subscription_id: String!, $id_plan: String!) {
update_subscription_status(
where: {id_account: {_eq: $accountId}},
_set: {
status: $status,
id_plan: $id_plan,
is_active: $is_active,
stripe_subscription_id: $stripe_subscription_id
}) {
affected_rows
returning {
id_account
id_plan
is_active
status
}
Expand Down
22 changes: 22 additions & 0 deletions graphql/query.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ query GetAccountInfoForCreatingSubscription ($id: uuid!) {
}
subscription_status {
status
stripe_subscription_id
subscription_plan {
stripe_code
}
Expand All @@ -28,4 +29,25 @@ query GetStripeSubscription ($id_account: uuid!) {
subscription_status(where: {id_account: {_eq: $id_account}}) {
stripe_subscription_id
}
}

## Subscription
query GetAccountFromSubscription ($stripe_subscription_id: String!) {
subscription_status(where: {stripe_subscription_id: {_eq: $stripe_subscription_id}}) {
id_account
}
}

## Subscription
query GetStripePlanFromPlan ( $id: String!) {
subscription_plan(where: {id: {_eq: $id}, is_active: {_eq: true}}) {
stripe_code
}
}

## Subscription
query GetPlanFromStripePlan ( $stripe_code: String!) {
subscription_plan(where: {stripe_code: {_eq: $stripe_code}, is_active: {_eq: true}}) {
id
}
}
99 changes: 86 additions & 13 deletions pkg/e2e/subscription_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func initSubscription() map[string]interface{} {
context.Background(),
fmt.Sprintf(`
mutation InitSubscription {
init_subscription ( data: { account_name: "%0s", id_plan: "%1s" } ) {
subscription_init ( data: { account_name: "%0s", id_plan: "%1s" } ) {
account_id
}
}`, generateAccountName(), "basic"),
Expand All @@ -68,7 +68,7 @@ func initSubscription() map[string]interface{} {
true,
&bodyInit,
)
accountID, _ := ask.For(bodyInit, "init_subscription.account_id").String("")
accountID, _ := ask.For(bodyInit, "subscription_init.account_id").String("")

Expect(err).To(BeNil())
Expect(accountID).NotTo(BeEmpty())
Expand All @@ -83,7 +83,7 @@ func createSubscription(a, cardN, cardM, cardY, cardCVC string) map[string]inter
context.Background(),
fmt.Sprintf(`
mutation CreateSubscription {
create_subscription ( data: { payment_method_id: "%0s" } ) {
subscription_create ( data: { payment_method_id: "%0s" } ) {
account_id,
is_active
}
Expand All @@ -97,7 +97,7 @@ func createSubscription(a, cardN, cardM, cardY, cardCVC string) map[string]inter
true,
&bodyCreate,
)
accountID, _ := ask.For(bodyCreate, "create_subscription.account_id").String("")
accountID, _ := ask.For(bodyCreate, "subscription_create.account_id").String("")

// Then
Expect(err).To(BeNil())
Expand All @@ -121,10 +121,10 @@ var _ = Describe("Subscription e2e", func() {
It("I should be able to execute complete payment flow with the default card", func() {

responseInit := initSubscription()
accountID, _ := ask.For(responseInit, "init_subscription.account_id").String("")
accountID, _ := ask.For(responseInit, "subscription_init.account_id").String("")

responseCreate := createSubscription(accountID, "4242424242424242", "01", "2030", "314")
isActive, _ := ask.For(responseCreate, "create_subscription.is_active").Bool(false)
isActive, _ := ask.For(responseCreate, "subscription_create.is_active").Bool(false)

Expect(isActive).To(BeTrue())

Expand All @@ -133,18 +133,91 @@ var _ = Describe("Subscription e2e", func() {
It("I should be able to execute a payment with 3d authentication", func() {

responseInit := initSubscription()
accountID, _ := ask.For(responseInit, "init_subscription.account_id").String("")
accountID, _ := ask.For(responseInit, "subscription_init.account_id").String("")

responseCreate := createSubscription(accountID, "4000002760003184", "01", "2030", "314")
isActive, _ := ask.For(responseCreate, "create_subscription.is_active").Bool(true)
isActive, _ := ask.For(responseCreate, "subscription_create.is_active").Bool(true)

Expect(isActive).To(BeFalse())
})

It("I should be able to retry when payment is not successful", func() {

responseInit := initSubscription()
accountID, _ := ask.For(responseInit, "subscription_init.account_id").String("")

responseCreate := createSubscription(accountID, "4000002760003184", "01", "2030", "314")
isActiveCreate, _ := ask.For(responseCreate, "subscription_create.is_active").Bool(true)
accountCreate, _ := ask.For(responseCreate, "subscription_create.account_id").String("account_id_create")

bodyRetry := map[string]interface{}{}
err := graphqlService.Execute(
context.Background(),
fmt.Sprintf(`
mutation RetrySubscription {
subscription_retry ( data: { payment_method_id: "%0s" } ) {
account_id,
is_active
}
}`, createPaymentMethod("4242424242424242", "01", "2030", "314")),
[]gqlreq.RequestHeader{
{Key: "x-hasura-account-id", Value: accountID},
{Key: "x-hasura-role", Value: "account_owner"},
{Key: "x-hasura-user-id", Value: "user1"},
},
[]gqlreq.RequestVar{},
true,
&bodyRetry,
)
isActiveRetry, _ := ask.For(bodyRetry, "subscription_retry.is_active").Bool(false)
accountRetry, _ := ask.For(bodyRetry, "subscription_retry.account_id").String("account_id_retry")

Expect(err).To(BeNil())
Expect(isActiveCreate).To(BeFalse())
Expect(isActiveRetry).To(BeTrue())
Expect(accountCreate).To(Equal(accountRetry))
})

It("I should be able to change an existing plan", func() {

responseInit := initSubscription()
accountID, _ := ask.For(responseInit, "subscription_init.account_id").String("")

responseCreate := createSubscription(accountID, "4242424242424242", "01", "2030", "314")
isActiveCreate, _ := ask.For(responseCreate, "subscription_create.is_active").Bool(false)
accountCreate, _ := ask.For(responseCreate, "subscription_create.account_id").String("account_id_create")

bodyChange := map[string]interface{}{}
err := graphqlService.Execute(
context.Background(),
`mutation ChangeSubscription {
subscription_change ( data: { id_plan: "premium" } ) {
account_id,
is_active
}
}`,
[]gqlreq.RequestHeader{
{Key: "x-hasura-account-id", Value: accountID},
{Key: "x-hasura-role", Value: "account_owner"},
{Key: "x-hasura-user-id", Value: "user1"},
},
[]gqlreq.RequestVar{},
true,
&bodyChange,
)
isActiveRetry, _ := ask.For(bodyChange, "subscription_change.is_active").Bool(false)
accountChange, _ := ask.For(bodyChange, "subscription_change.account_id").String("account_id_change")

Expect(err).To(BeNil())
Expect(isActiveCreate).To(BeTrue())
Expect(isActiveRetry).To(BeTrue())
Expect(accountCreate).To(Equal(accountChange))
})

It("I should be able to crete and cancel subscription", func() {

responseInit := initSubscription()
accountID, _ := ask.For(responseInit, "init_subscription.account_id").String("")
accountID, _ := ask.For(responseInit, "subscription_init.account_id").String("")

_ = createSubscription(accountID, "4242424242424242", "01", "2030", "314")

Expand All @@ -153,7 +226,7 @@ var _ = Describe("Subscription e2e", func() {
err := graphqlService.Execute(
context.Background(),
`mutation CancelSubscription {
cancel_subscription {
subscription_cancel {
status
}
}`,
Expand All @@ -166,7 +239,7 @@ var _ = Describe("Subscription e2e", func() {
true,
&bodyCancel,
)
status, _ := ask.For(bodyCancel, "cancel_subscription.status").String("")
status, _ := ask.For(bodyCancel, "subscription_cancel.status").String("")

Expect(err).To(BeNil())
Expect(status).To(Equal("canceled"))
Expand All @@ -175,7 +248,7 @@ var _ = Describe("Subscription e2e", func() {
It("I should be able to cancel subscription if it is not account owner", func() {

responseInit := initSubscription()
accountID, _ := ask.For(responseInit, "init_subscription.account_id").String("")
accountID, _ := ask.For(responseInit, "subscription_init.account_id").String("")

_ = createSubscription(accountID, "4242424242424242", "01", "2030", "314")

Expand All @@ -184,7 +257,7 @@ var _ = Describe("Subscription e2e", func() {
err := graphqlService.Execute(
context.Background(),
`mutation CancelSubscription {
cancel_subscription {
subscription_cancel {
status
}
}`,
Expand Down
Loading

0 comments on commit 7764073

Please sign in to comment.