Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle successful payment for uma #2731

Draft
wants to merge 6 commits into
base: delete-cancel-order-legacy
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion services/skus/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type stripeClient interface {
Session(ctx context.Context, id string, params *stripe.CheckoutSessionParams) (*stripe.CheckoutSession, error)
CreateSession(ctx context.Context, params *stripe.CheckoutSessionParams) (*stripe.CheckoutSession, error)
Subscription(ctx context.Context, id string, params *stripe.SubscriptionParams) (*stripe.Subscription, error)
CancelSub(ctx context.Context, id string, params *stripe.SubscriptionCancelParams) error
FindCustomer(ctx context.Context, email string) (*stripe.Customer, bool)
}

Expand Down Expand Up @@ -1666,8 +1667,11 @@ func (s *Service) processStripeNotificationTx(ctx context.Context, dbi sqlx.ExtC
}

paidt := time.Now()
if err := s.renewOrderStripe(ctx, dbi, ord, subID, expt, paidt); err != nil {
return err
}

return s.renewOrderStripe(ctx, dbi, ord, subID, expt, paidt)
return s.processStripeMtoA(ctx, dbi, ntf)

case ntf.shouldCancel():
oid, err := ntf.orderID()
Expand Down Expand Up @@ -2551,6 +2555,43 @@ func (s *Service) recreateStripeSession(ctx context.Context, dbi sqlx.ExecerCont
return sessID, nil
}

func (s *Service) processStripeMtoA(ctx context.Context, dbi sqlx.ExtContext, ntf *stripeNotification) error {
umaData, err := ntf.umaData()
if err != nil {
// Recover from the error is possible.
// Not all orders are migration orders, therefore must not fail.
if errors.Is(err, errStripeIncompleteUMAData) {
return nil
}

return err
}

// Cancel the order and subscription.
oid, err := uuid.FromString(umaData.orderID)
if err != nil {
return err
}

if err := s.cancelOrderTx(ctx, dbi, oid); err != nil {
return err
}

if err := s.stripeCl.CancelSub(ctx, umaData.stSubID, nil); err != nil {
if !isErrStripeNotFound(err) {
return err
}
}

if !hasCxUsedUMACoupon(ntf, umaData) {
return nil
}

// Update the customer metadata.

return nil
}

func newOrderNewForReq(req *model.CreateOrderRequestNew, items []model.OrderItem, merchID, status string) (*model.OrderNew, error) {
// Check for number of items to be above 0.
//
Expand Down
265 changes: 265 additions & 0 deletions services/skus/service_nonint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6050,6 +6050,271 @@ func TestHandleRedeemFnError(t *testing.T) {
}
}

func TestService_processStripeMtoA(t *testing.T) {
type tcGiven struct {
repo *repository.MockOrder
stcl *xstripe.MockClient
ntf *stripeNotification
}

type testCase struct {
name string
given tcGiven
exp error
}

tests := []testCase{
{
name: "skip_incomplete_uma_data",
given: tcGiven{
repo: &repository.MockOrder{},
stcl: &xstripe.MockClient{},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{Metadata: map[string]string{}},
},
},
},
},
},
},

{
name: "error_invalid_data",
given: tcGiven{
repo: &repository.MockOrder{},
stcl: &xstripe.MockClient{},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{},
},
},
},
exp: errStripeNoInvoiceLines,
},

{
name: "error_invalid_order_id",
given: tcGiven{
repo: &repository.MockOrder{},
stcl: &xstripe.MockClient{},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{
Metadata: map[string]string{
"uma__st_sub_id": "st_sub_id_01",
"uma__sub_id": "facade00-0000-4000-a000-000000000000",
"uma__order_id": "decade00-0000-4000-a000-00000000000",
},
},
},
},
},
},
},
exp: errors.New("uuid: incorrect UUID length: decade00-0000-4000-a000-00000000000"),
},

{
name: "error_cancel_order",
given: tcGiven{
repo: &repository.MockOrder{
FnSetStatus: func(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, status string) error {
return model.Error("something_went_wrong")
},
},
stcl: &xstripe.MockClient{},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{
Metadata: map[string]string{
"uma__st_sub_id": "st_sub_id_01",
"uma__sub_id": "facade00-0000-4000-a000-000000000000",
"uma__order_id": "decade00-0000-4000-a000-000000000000",
},
},
},
},
},
},
},
exp: model.Error("something_went_wrong"),
},

{
name: "error_cancel_sub",
given: tcGiven{
repo: &repository.MockOrder{},
stcl: &xstripe.MockClient{
FnCancelSub: func(ctx context.Context, id string, params *stripe.SubscriptionCancelParams) error {
return model.Error("something_went_wrong")
},
},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{
Metadata: map[string]string{
"uma__st_sub_id": "st_sub_id_01",
"uma__sub_id": "facade00-0000-4000-a000-000000000000",
"uma__order_id": "decade00-0000-4000-a000-000000000000",
},
},
},
},
},
},
},
exp: model.Error("something_went_wrong"),
},

{
name: "success_cancel_sub_not_found_no_coupon",
given: tcGiven{
repo: &repository.MockOrder{},
stcl: &xstripe.MockClient{
FnCancelSub: func(ctx context.Context, id string, params *stripe.SubscriptionCancelParams) error {
rerr := &stripe.Error{
HTTPStatusCode: http.StatusNotFound,
Code: stripe.ErrorCodeResourceMissing,
}

return rerr
},
},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{
Metadata: map[string]string{
"uma__st_sub_id": "st_sub_id_01",
"uma__sub_id": "facade00-0000-4000-a000-000000000000",
"uma__order_id": "decade00-0000-4000-a000-000000000000",
},
},
},
},
},
},
},
},

{
name: "success_no_coupon",
given: tcGiven{
repo: &repository.MockOrder{
FnSetStatus: func(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, status string) error {
if !uuid.Equal(id, uuid.Must(uuid.FromString("decade00-0000-4000-a000-000000000000"))) {
return model.Error("unexpected_cancel_order_id")
}

if status != model.OrderStatusCanceled {
return model.Error("unexpected_cancel_order_status")
}

return nil
},
},
stcl: &xstripe.MockClient{
FnCancelSub: func(ctx context.Context, id string, params *stripe.SubscriptionCancelParams) error {
if id != "st_sub_id_01" {
return model.Error("unexpected_cancel_sub_id")
}

return nil
},
},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{
Metadata: map[string]string{
"uma__st_sub_id": "st_sub_id_01",
"uma__sub_id": "facade00-0000-4000-a000-000000000000",
"uma__order_id": "decade00-0000-4000-a000-000000000000",
},
},
},
},
},
},
},
},

{
name: "success",
given: tcGiven{
repo: &repository.MockOrder{
FnSetStatus: func(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, status string) error {
if !uuid.Equal(id, uuid.Must(uuid.FromString("decade00-0000-4000-a000-000000000000"))) {
return model.Error("unexpected_cancel_order_id")
}

if status != model.OrderStatusCanceled {
return model.Error("unexpected_cancel_order_status")
}

return nil
},
},
stcl: &xstripe.MockClient{
FnCancelSub: func(ctx context.Context, id string, params *stripe.SubscriptionCancelParams) error {
if id != "st_sub_id_01" {
return model.Error("unexpected_cancel_sub_id")
}

return nil
},
},
ntf: &stripeNotification{
invoice: &stripe.Invoice{
Lines: &stripe.InvoiceLineList{
Data: []*stripe.InvoiceLine{
{
Metadata: map[string]string{
"uma__st_sub_id": "st_sub_id_01",
"uma__sub_id": "facade00-0000-4000-a000-000000000000",
"uma__order_id": "decade00-0000-4000-a000-000000000000",
"uma__coupon_id": "coup_id_01",
},
},
},
},
Discount: &stripe.Discount{
Coupon: &stripe.Coupon{ID: "coup_id_01"},
},
},
},
},
},
}

for i := range tests {
tc := tests[i]

t.Run(tc.name, func(t *testing.T) {
svc := &Service{
orderRepo: tc.given.repo,
stripeCl: tc.given.stcl,
}

ctx := context.Background()

actual := svc.processStripeMtoA(ctx, nil, tc.given.ntf)
should.Equal(t, tc.exp, actual)
})
}
}

type mockRadomClient struct {
fnCreateCheckoutSession func(ctx context.Context, creq *radom.CheckoutSessionRequest) (radom.CheckoutSessionResponse, error)
fnGetSubscription func(ctx context.Context, subID string) (*radom.SubscriptionResponse, error)
Expand Down
Loading
Loading