Skip to content

Commit ade3a3d

Browse files
committed
Add the frontend
1 parent 473f511 commit ade3a3d

File tree

12 files changed

+1016
-69
lines changed

12 files changed

+1016
-69
lines changed

api/docs/docs.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2380,7 +2380,18 @@ const docTemplate = `{
23802380
"tags": [
23812381
"Users"
23822382
],
2383-
"summary": "Generate a subscription invoice",
2383+
"summary": "Generate a subscription payment invoice",
2384+
"parameters": [
2385+
{
2386+
"description": "Generate subscription payment invoice parameters",
2387+
"name": "payload",
2388+
"in": "body",
2389+
"required": true,
2390+
"schema": {
2391+
"$ref": "#/definitions/requests.UserPaymentInvoice"
2392+
}
2393+
}
2394+
],
23842395
"responses": {
23852396
"200": {
23862397
"description": "OK",
@@ -3798,6 +3809,48 @@ const docTemplate = `{
37983809
}
37993810
}
38003811
},
3812+
"requests.UserPaymentInvoice": {
3813+
"type": "object",
3814+
"required": [
3815+
"address",
3816+
"city",
3817+
"country",
3818+
"name",
3819+
"notes",
3820+
"state",
3821+
"zip_code"
3822+
],
3823+
"properties": {
3824+
"address": {
3825+
"type": "string",
3826+
"example": "221B Baker Street, London"
3827+
},
3828+
"city": {
3829+
"type": "string",
3830+
"example": "Los Angeles"
3831+
},
3832+
"country": {
3833+
"type": "string",
3834+
"example": "US"
3835+
},
3836+
"name": {
3837+
"type": "string",
3838+
"example": "Acme Corp"
3839+
},
3840+
"notes": {
3841+
"type": "string",
3842+
"example": "Thank you for your business!"
3843+
},
3844+
"state": {
3845+
"type": "string",
3846+
"example": "CA"
3847+
},
3848+
"zip_code": {
3849+
"type": "string",
3850+
"example": "9800"
3851+
}
3852+
}
3853+
},
38013854
"requests.UserUpdate": {
38023855
"type": "object",
38033856
"required": [

api/docs/swagger.json

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2156,7 +2156,18 @@
21562156
"consumes": ["application/json"],
21572157
"produces": ["application/pdf"],
21582158
"tags": ["Users"],
2159-
"summary": "Generate a subscription invoice",
2159+
"summary": "Generate a subscription payment invoice",
2160+
"parameters": [
2161+
{
2162+
"description": "Generate subscription payment invoice parameters",
2163+
"name": "payload",
2164+
"in": "body",
2165+
"required": true,
2166+
"schema": {
2167+
"$ref": "#/definitions/requests.UserPaymentInvoice"
2168+
}
2169+
}
2170+
],
21602171
"responses": {
21612172
"200": {
21622173
"description": "OK",
@@ -3477,6 +3488,48 @@
34773488
}
34783489
}
34793490
},
3491+
"requests.UserPaymentInvoice": {
3492+
"type": "object",
3493+
"required": [
3494+
"address",
3495+
"city",
3496+
"country",
3497+
"name",
3498+
"notes",
3499+
"state",
3500+
"zip_code"
3501+
],
3502+
"properties": {
3503+
"address": {
3504+
"type": "string",
3505+
"example": "221B Baker Street, London"
3506+
},
3507+
"city": {
3508+
"type": "string",
3509+
"example": "Los Angeles"
3510+
},
3511+
"country": {
3512+
"type": "string",
3513+
"example": "US"
3514+
},
3515+
"name": {
3516+
"type": "string",
3517+
"example": "Acme Corp"
3518+
},
3519+
"notes": {
3520+
"type": "string",
3521+
"example": "Thank you for your business!"
3522+
},
3523+
"state": {
3524+
"type": "string",
3525+
"example": "CA"
3526+
},
3527+
"zip_code": {
3528+
"type": "string",
3529+
"example": "9800"
3530+
}
3531+
}
3532+
},
34803533
"requests.UserUpdate": {
34813534
"type": "object",
34823535
"required": ["active_phone_id", "timezone"],

api/docs/swagger.yaml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,38 @@ definitions:
742742
- newsletter_enabled
743743
- webhook_enabled
744744
type: object
745+
requests.UserPaymentInvoice:
746+
properties:
747+
address:
748+
example: 221B Baker Street, London
749+
type: string
750+
city:
751+
example: Los Angeles
752+
type: string
753+
country:
754+
example: US
755+
type: string
756+
name:
757+
example: Acme Corp
758+
type: string
759+
notes:
760+
example: Thank you for your business!
761+
type: string
762+
state:
763+
example: CA
764+
type: string
765+
zip_code:
766+
example: "9800"
767+
type: string
768+
required:
769+
- address
770+
- city
771+
- country
772+
- name
773+
- notes
774+
- state
775+
- zip_code
776+
type: object
745777
requests.UserUpdate:
746778
properties:
747779
active_phone_id:
@@ -2930,6 +2962,13 @@ paths:
29302962
description:
29312963
Generates a new invoice PDF file for the given subscription payment
29322964
with given parameters.
2965+
parameters:
2966+
- description: Generate subscription payment invoice parameters
2967+
in: body
2968+
name: payload
2969+
required: true
2970+
schema:
2971+
$ref: "#/definitions/requests.UserPaymentInvoice"
29332972
produces:
29342973
- application/pdf
29352974
responses:
@@ -2955,7 +2994,7 @@ paths:
29552994
$ref: "#/definitions/responses.InternalServerError"
29562995
security:
29572996
- ApiKeyAuth: []
2958-
summary: Generate a subscription invoice
2997+
summary: Generate a subscription payment invoice
29592998
tags:
29602999
- Users
29613000
/users/subscription/payments:

api/pkg/di/container.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ func (container *Container) UserHandlerValidator() (validator *validators.UserHa
648648
return validators.NewUserHandlerValidator(
649649
container.Logger(),
650650
container.Tracer(),
651+
container.UserService(),
651652
)
652653
}
653654

@@ -934,6 +935,7 @@ func (container *Container) UserService() (service *services.UserService) {
934935
container.LemonsqueezyClient(),
935936
container.EventDispatcher(),
936937
container.FirebaseAuthClient(),
938+
container.HTTPClient("lemonsqueezy"),
937939
)
938940
}
939941

api/pkg/handlers/billing_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (h *BillingHandler) UsageHistory(c *fiber.Ctx) error {
6565

6666
var request requests.BillingUsageHistory
6767
if err := c.QueryParser(&request); err != nil {
68-
msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request)
68+
msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.Body(), request)
6969
ctxLogger.Warn(stacktrace.Propagate(err, msg))
7070
return h.responseBadRequest(c, err)
7171
}

api/pkg/handlers/user_handler.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func (h *UserHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.H
4646
router.Put("/v1/users/:userID/notifications", h.computeRoute(middlewares, h.UpdateNotifications)...)
4747
router.Get("/v1/users/subscription-update-url", h.computeRoute(middlewares, h.subscriptionUpdateURL)...)
4848
router.Delete("/v1/users/subscription", h.computeRoute(middlewares, h.cancelSubscription)...)
49-
router.Get("/v1/users/subscription/invoices", h.computeRoute(middlewares, h.subscriptionPayments)...)
49+
router.Get("/v1/users/subscription/payments", h.computeRoute(middlewares, h.subscriptionPayments)...)
5050
router.Post("/v1/users/subscription/invoices/:subscriptionInvoiceID", h.computeRoute(middlewares, h.subscriptionInvoice)...)
5151
}
5252

@@ -161,11 +161,9 @@ func (h *UserHandler) Delete(c *fiber.Ctx) error {
161161
// @Failure 500 {object} responses.InternalServerError
162162
// @Router /users/{userID}/notifications [put]
163163
func (h *UserHandler) UpdateNotifications(c *fiber.Ctx) error {
164-
ctx, span := h.tracer.StartFromFiberCtx(c)
164+
ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
165165
defer span.End()
166166

167-
ctxLogger := h.tracer.CtxLogger(h.logger, span)
168-
169167
var request requests.UserNotificationUpdate
170168
if err := c.BodyParser(&request); err != nil {
171169
msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request)
@@ -303,12 +301,13 @@ func (h *UserHandler) subscriptionPayments(c *fiber.Ctx) error {
303301
}
304302

305303
// subscriptionInvoice generates an invoice for a given subscription invoice ID
306-
// @Summary Generate a subscription invoice
304+
// @Summary Generate a subscription payment invoice
307305
// @Description Generates a new invoice PDF file for the given subscription payment with given parameters.
308306
// @Security ApiKeyAuth
309307
// @Tags Users
310308
// @Accept json
311309
// @Produce application/pdf
310+
// @Param payload body requests.UserPaymentInvoice true "Generate subscription payment invoice parameters"
312311
// @Success 200 {file} file
313312
// @Failure 400 {object} responses.BadRequest
314313
// @Failure 401 {object} responses.Unauthorized
@@ -319,17 +318,29 @@ func (h *UserHandler) subscriptionInvoice(c *fiber.Ctx) error {
319318
ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
320319
defer span.End()
321320

322-
invoiceID := c.Params("subscriptionInvoiceID")
321+
var request requests.UserPaymentInvoice
322+
if err := c.BodyParser(&request); err != nil {
323+
msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.Body(), request)
324+
ctxLogger.Warn(stacktrace.Propagate(err, msg))
325+
return h.responseBadRequest(c, err)
326+
}
327+
328+
request.SubscriptionInvoiceID = c.Params("subscriptionInvoiceID")
329+
if errors := h.validator.ValidatePaymentInvoice(ctx, h.userIDFomContext(c), request.Sanitize()); len(errors) != 0 {
330+
msg := fmt.Sprintf("validation errors [%s], while validating subscription payment invoice request [%s]", spew.Sdump(errors), c.Body())
331+
ctxLogger.Warn(stacktrace.NewError(msg))
332+
return h.responseUnprocessableEntity(c, errors, "validation errors while generating payment invoice")
333+
}
323334

324-
data, err := h.service.GenerateReceipt(ctx, h.userIDFomContext(c))
335+
data, err := h.service.GenerateReceipt(ctx, request.UserInvoiceGenerateParams(h.userIDFomContext(c)))
325336
if err != nil {
326-
msg := fmt.Sprintf("cannot generate receipt for invoice ID [%s] and user [%s]", invoiceID, h.userFromContext(c))
337+
msg := fmt.Sprintf("cannot generate receipt for invoice ID [%s] and user [%s]", request.SubscriptionInvoiceID, h.userFromContext(c))
327338
ctxLogger.Error(stacktrace.Propagate(err, msg))
328339
return h.responseInternalServerError(c)
329340
}
330341

331342
c.Set(fiber.HeaderContentType, "application/pdf")
332-
c.Set(fiber.HeaderContentDisposition, fmt.Sprintf("attachment; filename=\"%s.pdf\"", invoiceID))
343+
c.Set(fiber.HeaderContentDisposition, fmt.Sprintf("attachment; filename=\"httpsms.com - %s.pdf\"", request.SubscriptionInvoiceID))
333344

334345
return c.SendStream(data)
335346
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package requests
2+
3+
import (
4+
"github.com/NdoleStudio/httpsms/pkg/entities"
5+
"github.com/NdoleStudio/httpsms/pkg/services"
6+
)
7+
8+
// UserPaymentInvoice is the payload for generating a subscription payment invoice
9+
type UserPaymentInvoice struct {
10+
request
11+
Name string `json:"name" example:"Acme Corp"`
12+
Address string `json:"address" example:"221B Baker Street, London"`
13+
City string `json:"city" example:"Los Angeles"`
14+
State string `json:"state" example:"CA"`
15+
Country string `json:"country" example:"US"`
16+
ZipCode string `json:"zip_code" example:"9800"`
17+
Notes string `json:"notes" example:"Thank you for your business!"`
18+
SubscriptionInvoiceID string `json:"subscriptionInvoiceID" swaggerignore:"true"` // used internally for validation
19+
}
20+
21+
// Sanitize sets defaults to MessageReceive
22+
func (input *UserPaymentInvoice) Sanitize() UserPaymentInvoice {
23+
input.Name = input.sanitizeAddress(input.Name)
24+
input.Address = input.sanitizeAddress(input.Address)
25+
input.City = input.sanitizeAddress(input.City)
26+
input.State = input.sanitizeAddress(input.State)
27+
input.Country = input.sanitizeAddress(input.Country)
28+
input.ZipCode = input.sanitizeAddress(input.ZipCode)
29+
input.Notes = input.sanitizeAddress(input.Notes)
30+
return *input
31+
}
32+
33+
// UserInvoiceGenerateParams converts UserPaymentInvoice to services.UserInvoiceGenerateParams
34+
func (input *UserPaymentInvoice) UserInvoiceGenerateParams(userID entities.UserID) *services.UserInvoiceGenerateParams {
35+
return &services.UserInvoiceGenerateParams{
36+
UserID: userID,
37+
SubscriptionInvoiceID: input.SubscriptionInvoiceID,
38+
Name: input.Name,
39+
Address: input.Address,
40+
City: input.City,
41+
State: input.State,
42+
Country: input.Country,
43+
Notes: input.Notes,
44+
ZipCode: input.ZipCode,
45+
}
46+
}

0 commit comments

Comments
 (0)