Skip to content

Commit

Permalink
Stripe: creates GET endpoint for customer portal session
Browse files Browse the repository at this point in the history
  • Loading branch information
emmdim committed Dec 24, 2024
1 parent 1ef2a50 commit 584d220
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 0 deletions.
3 changes: 3 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ func (a *API) initRouter() http.Handler {
// get stripe checkout session info
log.Infow("new route", "method", "GET", "path", subscriptionsCheckoutSession)
r.Get(subscriptionsCheckoutSession, a.checkoutSessionHandler)
// get stripe subscription portal session info
log.Infow("new route", "method", "GET", "path", subscriptionsPortal)
r.Get(subscriptionsPortal, a.createSubscriptionPortalSessionHandler)
})

// Public routes
Expand Down
23 changes: 23 additions & 0 deletions api/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- [🔰 Subscriptions](#-subscriptions)
- [🛒 Create Checkout session](#-create-checkout-session)
- [🛍️ Get Checkout session info](#-get-checkout-session-info)
- [🔗 Create Subscription Portal Session](#-create-subscription-portal-session)

</details>

Expand Down Expand Up @@ -971,4 +972,26 @@ This request can be made only by organization admins.
|:---:|:---:|:---|
| `400` | `40010` | `malformed URL parameter` |
| `400` | `40023` | `session not found` |
| `500` | `50002` | `internal server error` |

### 🔗 Create Subscription Portal Session

* **Path** `/subscriptions/{orgAddress}/portal`
* **Method** `GET`
* **Headers**
* `Authentication: Bearer <user_token>`

* **Response**
```json
{
"portalURL": "https://portal.stripe.com/session/..."
}
```

* **Errors**

| HTTP Status | Error code | Message |
|:---:|:---:|:---|
| `401` | `40001` | `user not authorized` |
| `400` | `40011` | `no organization provided` |
| `500` | `50002` | `internal server error` |
2 changes: 2 additions & 0 deletions api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ const (
subscriptionsCheckout = "/subscriptions/checkout"
// GET /subscriptions/checkout/{sessionID} to get the checkout session information
subscriptionsCheckoutSession = "/subscriptions/checkout/{sessionID}"
// GET /subscriptions/portal to get the stripe subscription portal URL
subscriptionsPortal = "/subscriptions/{address}/portal/"
)
33 changes: 33 additions & 0 deletions api/stripe.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,36 @@ func (a *API) getSubscriptionOrgInfo(event *stripe.Event) (*stripeService.Stripe

return stripeSubscriptionInfo, org, nil
}

// createSubscriptionPortalSessionHandler handles the creation of a Stripe customer portal session
// based on the organization creator email..
// It requires the user to be authenticated and to have admin role for the organization.
func (a *API) createSubscriptionPortalSessionHandler(w http.ResponseWriter, r *http.Request) {
user, ok := userFromContext(r.Context())
if !ok {
ErrUnauthorized.Write(w)
return
}
// get the organization info from the request context
org, _, ok := a.organizationFromRequest(r)
if !ok {
ErrNoOrganizationProvided.Write(w)
return
}
if !user.HasRoleFor(org.Address, db.AdminRole) {
ErrUnauthorized.Withf("user is not admin of organization").Write(w)
return
}
session, err := a.stripe.CreatePortalSession(org.Creator)
if err != nil {
ErrStripeError.Withf("Cannot create customer portal session: %v", err).Write(w)
return
}

data := &struct {
PortalURL string `json:"portalURL"`
}{
PortalURL: session.URL,
}
httpWriteJSON(w, data)
}
20 changes: 20 additions & 0 deletions stripe/stripe.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/stripe/stripe-go/v81"
portalSession "github.com/stripe/stripe-go/v81/billingportal/session"
"github.com/stripe/stripe-go/v81/checkout/session"
"github.com/stripe/stripe-go/v81/customer"
"github.com/stripe/stripe-go/v81/price"
Expand Down Expand Up @@ -291,3 +292,22 @@ func (s *StripeClient) RetrieveCheckoutSession(sessionID string) (*ReturnStatus,
}
return data, nil
}

// CreatePortalSession creates a new billing portal session for a customer based on an email address.
func (s *StripeClient) CreatePortalSession(customerEmail string) (*stripe.BillingPortalSession, error) {
// get stripe customer based on provided email
customerParams := &stripe.CustomerListParams{
Email: stripe.String(customerEmail),
}
var customerID string
if customers := customer.List(customerParams); customers.Next() {
customerID = customers.Customer().ID
} else {
return nil, fmt.Errorf("could not find customer with email %s", customerEmail)
}

params := &stripe.BillingPortalSessionParams{
Customer: &customerID,
}
return portalSession.New(params)
}

0 comments on commit 584d220

Please sign in to comment.