func TestCreateAndGetGroup(t *testing.T) { db := modeltesthelper.NewMongoDB(t) defer db.Close() g, err := createGroup() if err != nil { t.Fatalf(err.Error()) } g2, err := modelhelper.GetGroup(g.Slug) if err != nil { t.Errorf(err.Error()) } if g2 == nil { t.Errorf("couldnt fetch group by its slug. Got nil, expected: %+v", g) } if g2.Id.Hex() != g.Id.Hex() { t.Errorf("groups are not same: expected: %+v, got: %+v ", g.Id.Hex(), g2.Id.Hex()) } randomName := bson.NewObjectId().Hex() _, err = modelhelper.GetGroup(randomName) if err == nil { t.Errorf("we should not be able to find the group") } }
// HandleParticipant handles participant operations func (c *Controller) HandleParticipant(cp *models.ChannelParticipant) error { channel, err := models.Cache.Channel.ById(cp.ChannelId) if err != nil { c.log.Error("Channel: %d is not found", cp.ChannelId) return nil } if channel.TypeConstant != models.Channel_TYPE_GROUP { return nil // following logic ensures that channel is a group channel } group, err := modelhelper.GetGroup(channel.GroupName) if err != nil && err != mgo.ErrNotFound { return err } if err == mgo.ErrNotFound { c.log.Error("Group: %s is not found in mongo", channel.GroupName) return nil } if err := c.handleDefaultChannels(group.DefaultChannels, cp); err != nil { return err } if err := c.handleParticipantRemove(cp); err != nil { return err } return nil }
// UpdateCustomerForGroup updates customer data of a group func UpdateCustomerForGroup(username, groupName string, params *stripe.CustomerParams) (*stripe.Customer, error) { if _, err := EnsureCustomerForGroup(username, groupName, params); err != nil { return nil, err } group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Payment.Customer.ID == "" { return nil, ErrCustomerNotExists } params, err = populateCustomerParams(username, groupName, params) if err != nil { return nil, err } // if the update request has a new CC, delete old ones. if params != nil && params.Source != nil && params.Source.Token != "" { if err := DeleteCreditCardForGroup(groupName); err != nil { return nil, err } } return customer.Update(group.Payment.Customer.ID, params) }
func getGroupBySlug(slug string) bool { if isIn(slug, "koding", "guests", "team") { return true } if _, err := existingGroupBySlug.Get(slug); err == nil { return true } if _, err := deletedGroupBySlug.Get(slug); err == nil { return false } _, err := helper.GetGroup(slug) if err == mgo.ErrNotFound { deletedGroupBySlug.Set(slug, struct{}{}) return false } // treat them as existing on random errors if err != nil { fmt.Printf("err while getting group by slug %q, %s\n", slug, err) return true } existingGroupBySlug.Set(slug, struct{}{}) return true }
// HasCreditCard returns the existance status of group's credit card func HasCreditCard(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { if !context.IsLoggedIn() { return response.NewBadRequest(models.ErrNotLoggedIn) } group, err := modelhelper.GetGroup(context.GroupName) if err != nil { return response.NewBadRequest(err) } if group.Payment.Customer.ID == "" { return response.NewNotFound() } err = payment.CheckCustomerHasSource(group.Payment.Customer.ID) if err == payment.ErrCustomerSourceNotExists { return response.NewNotFound() } if err != nil { return response.NewBadRequest(err) } return response.NewDefaultOK() }
// ListInvoicesForGroup lists invoices of a group func ListInvoicesForGroup(groupName string, startingAfter string, limit int) ([]*stripe.Invoice, error) { group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Payment.Customer.ID == "" { return nil, ErrCustomerNotExists } var invoices []*stripe.Invoice params := &stripe.InvoiceListParams{} params.Customer = group.Payment.Customer.ID params.Limit = limit params.Start = startingAfter i := invoice.List(params) for i.Next() { invoices = append(invoices, i.Invoice()) } if err := i.Err(); err != nil { return nil, err } return invoices, nil }
func (cmd *GroupStack) details(username string) (*modelhelper.StackDetails, error) { user, err := modelhelper.GetUser(username) if err != nil { return nil, fmt.Errorf("unable to find a user %q: %s", username, err) } group, err := modelhelper.GetGroup(cmd.groupSlug) if err != nil { return nil, fmt.Errorf("unable to find a group %q: %s", cmd.groupSlug, err) } machine, err := modelhelper.GetMachineBySlug(user.ObjectId, cmd.machineSlug) if err != nil { return nil, fmt.Errorf("unable to find a machine slug=%q, userID=%q: %s", cmd.machineSlug, user.ObjectId.Hex(), err) } account, err := modelhelper.GetAccount(username) if err != nil { return nil, fmt.Errorf("unable to find an account for %q: %s", username, err) } sd := &modelhelper.StackDetails{ UserID: user.ObjectId, AccountID: account.Id, GroupID: group.Id, UserName: user.Name, GroupSlug: group.Slug, MachineID: machine.ObjectId, BaseID: bson.ObjectIdHex(cmd.baseID), } return sd, nil }
// EnsureCustomerForGroup registers a customer for a group if it does not exist, // returns the existing one if created previously func EnsureCustomerForGroup(username string, groupName string, req *stripe.CustomerParams) (*stripe.Customer, error) { group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } // if we already have the customer, return it. if group.Payment.Customer.ID != "" { return customer.Get(group.Payment.Customer.ID, nil) } req, err = populateCustomerParams(username, group.Slug, req) if err != nil { return nil, err } cus, err := customer.New(req) if err != nil { return nil, err } if err := modelhelper.UpdateGroupPartial( modelhelper.Selector{"_id": group.Id}, modelhelper.Selector{ "$set": modelhelper.Selector{ "payment.customer.id": cus.ID, }, }, ); err != nil { return nil, err } return cus, nil }
func getGroupPaymentStatusFromCache(groupName string) (string, error) { data, err := groupCache.Get(groupName) if err != nil && err != cache.ErrNotFound { return "", err } if err == nil { status, ok := data.(string) if ok { return status, nil } } group, err := modelhelper.GetGroup(groupName) if err != nil { return "", err } status := group.Payment.Subscription.Status // set defaul payment status if status != mongomodels.SubStatusActive { status = "invalid" } if err := groupCache.Set(groupName, status); err != nil { return "", err } return string(status), nil }
func withSubscription(endpoint, groupName, sessionID, planID string, f func(subscriptionID string)) { createURL := endpoint + EndpointSubscriptionCreate deleteURL := endpoint + EndpointSubscriptionCancel group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) Convey("We should be able to create a subscription", func() { req, err := json.Marshal(&stripe.SubParams{ Customer: group.Payment.Customer.ID, Plan: planID, }) tests.ResultedWithNoErrorCheck(req, err) res, err := rest.DoRequestWithAuth("POST", createURL, req, sessionID) tests.ResultedWithNoErrorCheck(res, err) v := &stripe.Sub{} err = json.Unmarshal(res, v) So(err, ShouldBeNil) f(v.ID) Convey("We should be able to cancel the subscription", func() { res, err = rest.DoRequestWithAuth("DELETE", deleteURL, req, sessionID) tests.ResultedWithNoErrorCheck(res, err) v = &stripe.Sub{} err = json.Unmarshal(res, v) So(err, ShouldBeNil) So(v.Status, ShouldEqual, "canceled") }) }) }
func (mwc *Controller) createGroupChannel(groupName string) (*models.Channel, error) { c := models.NewChannel() c.Name = groupName if groupName == models.Channel_KODING_NAME { c.Name = "public" } c.GroupName = groupName c.TypeConstant = models.Channel_TYPE_GROUP group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Visibility == "visible" { c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC } else { c.PrivacyConstant = models.Channel_PRIVACY_PRIVATE } // find group owner creatorId, err := mwc.fetchGroupOwnerId(group) if err != nil { return nil, err } c.CreatorId = creatorId // create channel if err := c.Create(); err != nil { return nil, err } return c, nil }
// DeleteSubscriptionForGroup deletes the subscription of a group func DeleteSubscriptionForGroup(groupName string) (*stripe.Sub, error) { group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Payment.Subscription.ID == "" { return nil, ErrCustomerNotSubscribedToAnyPlans } if group.Payment.Customer.ID == "" { return nil, ErrCustomerNotExists } sub, err := deleteSubscription(group.Payment.Subscription.ID, group.Payment.Customer.ID) if err != nil { return nil, err } if err := syncGroupWithCustomerID(group.Payment.Customer.ID); err != nil { return nil, err } return sub, nil }
func TestCreditCardInfoNotSubscribingMember(t *testing.T) { Convey("When a non subscribed user request to get CC", t, func() { withTestServer(t, func(endpoint string) { withStubData(endpoint, func(username, groupName, sessionID string) { group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) err = modelhelper.UpdateGroupPartial( modelhelper.Selector{"_id": group.Id}, modelhelper.Selector{ "$unset": modelhelper.Selector{"payment.customer.id": ""}, }, ) So(err, ShouldBeNil) Convey("Endpoint should return error", func() { _, err := rest.DoRequestWithAuth("GET", endpoint+EndpointCreditCardHas, nil, sessionID) So(err, ShouldNotBeNil) // set the customer id back becase test data callback requires it. err = modelhelper.UpdateGroupPartial( modelhelper.Selector{"_id": group.Id}, modelhelper.Selector{ "$set": modelhelper.Selector{"payment.customer.id": group.Payment.Customer.ID}, }, ) So(err, ShouldBeNil) }) }) }) }) }
func TestInvoiceList(t *testing.T) { Convey("Given stub data", t, func() { withTestServer(t, func(endpoint string) { withStubData(endpoint, func(username, groupName, sessionID string) { withTestPlan(func(planID string) { createURL := endpoint + EndpointSubscriptionCreate deleteURL := endpoint + EndpointSubscriptionCancel group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) addCreditCardToUserWithChecks(endpoint, sessionID) Convey("We should be able to create a subscription", func() { req, err := json.Marshal(&stripe.SubParams{ Customer: group.Payment.Customer.ID, Plan: planID, }) tests.ResultedWithNoErrorCheck(req, err) res, err := rest.DoRequestWithAuth("POST", createURL, req, sessionID) tests.ResultedWithNoErrorCheck(res, err) v := &stripe.Sub{} err = json.Unmarshal(res, v) So(err, ShouldBeNil) So(v.Status, ShouldEqual, "active") Convey("We should be able to list invoices", func() { listInvoicesURL := endpoint + EndpointInvoiceList res, err = rest.DoRequestWithAuth("GET", listInvoicesURL, nil, sessionID) tests.ResultedWithNoErrorCheck(res, err) var invoices []*stripe.Invoice err = json.Unmarshal(res, &invoices) So(err, ShouldBeNil) So(len(invoices), ShouldBeGreaterThan, 0) Convey("We should be able to list invoices with startingAfter query param", func() { listInvoicesURLWithQuery := fmt.Sprintf("%s%s?startingAfter=%s", endpoint, EndpointInvoiceList, invoices[0].ID) res, err = rest.DoRequestWithAuth("GET", listInvoicesURLWithQuery, nil, sessionID) tests.ResultedWithNoErrorCheck(res, err) var invoices []*stripe.Invoice err = json.Unmarshal(res, &invoices) So(err, ShouldBeNil) So(len(invoices), ShouldEqual, 0) // because we only have one invoice Convey("We should be able to cancel the subscription", func() { res, err = rest.DoRequestWithAuth("DELETE", deleteURL, req, sessionID) tests.ResultedWithNoErrorCheck(res, err) }) }) }) }) }) }) }) }) }
// BuildTeam fetches team details from MongoDB. // // When it returns with nil error, the b.Team field is // guranteed to be non-nil. func (b *Builder) BuildTeam(team string) error { g, err := modelhelper.GetGroup(team) if err != nil { return err } b.Team = g return nil }
// make sure we can subscribe to 7 days-free plan with more trial period func TestSubscribingToPaidPlanWithWithDifferentTrialPeriodThanDefault(t *testing.T) { Convey("Given stub data", t, func() { withTestServer(t, func(endpoint string) { withStubData(endpoint, func(username, groupName, sessionID string) { pp := &stripe.PlanParams{ Amount: 12345, Interval: stripeplan.Month, IntervalCount: 1, TrialPeriod: 1, // trial for one day Name: fmt.Sprintf("plan for %s", username), Currency: currency.USD, ID: fmt.Sprintf("plan_for_%s", username), Statement: "NAN-FREE", } plan, err := stripeplan.New(pp) So(err, ShouldBeNil) addCreditCardToUserWithChecks(endpoint, sessionID) createURL := endpoint + EndpointSubscriptionCreate deleteURL := endpoint + EndpointSubscriptionCancel group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) req, err := json.Marshal(&stripe.SubParams{ Customer: group.Payment.Customer.ID, Plan: plan.ID, TrialEnd: time.Now().Add(time.Hour * 48).Unix(), }) tests.ResultedWithNoErrorCheck(req, err) sub, err := rest.DoRequestWithAuth("POST", createURL, req, sessionID) tests.ResultedWithNoErrorCheck(sub, err) v := &stripe.Sub{} err = json.Unmarshal(sub, v) So(err, ShouldBeNil) So(v.TrialEnd, ShouldBeGreaterThan, time.Now().Add(time.Hour*24*time.Duration(pp.TrialPeriod)).Unix()) Convey("We should be able to cancel the subscription", func() { res, err := rest.DoRequestWithAuth("DELETE", deleteURL, req, sessionID) tests.ResultedWithNoErrorCheck(res, err) v := &stripe.Sub{} err = json.Unmarshal(res, v) So(err, ShouldBeNil) So(v.Status, ShouldEqual, "canceled") }) }) }) }) }
// GetCustomerForGroup get the registered customer info of a group if exists func GetCustomerForGroup(groupName string) (*stripe.Customer, error) { group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Payment.Customer.ID == "" { return nil, ErrCustomerNotExists } return customer.Get(group.Payment.Customer.ID, nil) }
// GetSubscriptionForGroup gets the subscription of a group func GetSubscriptionForGroup(groupName string) (*stripe.Sub, error) { group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Payment.Subscription.ID == "" { return nil, ErrCustomerNotSubscribedToAnyPlans } return sub.Get(group.Payment.Subscription.ID, nil) }
// DeleteCreditCardForGroup deletes credit card of the group, if customer is not // registered yet for the group, returns error. Credit card operations hanled by // Stripe. func DeleteCreditCardForGroup(groupName string) error { group, err := modelhelper.GetGroup(groupName) if err != nil { return err } if group.Payment.Customer.ID == "" { return ErrCustomerNotExists } return deleteCreditCard(group.Payment.Customer.ID) }
func setDefaults(log logging.Logger) { group, err := modelhelper.GetGroup(models.Channel_KODING_NAME) if err != nil { log.Error("err while fetching koding group: %s", err.Error()) return } log.Debug("mongo group found") setPublicChannel(log, group) setChangeLogChannel(log, group) log.Info("socialApi defaults are created") }
func withStubData(endpoint string, f func(username string, groupName string, sessionID string)) { acc, _, groupName := models.CreateRandomGroupDataWithChecks() group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) err = modelhelper.MakeAdmin(bson.ObjectIdHex(acc.OldId), group.Id) So(err, ShouldBeNil) ses, err := modelhelper.FetchOrCreateSession(acc.Nick, groupName) tests.ResultedWithNoErrorCheck(ses, err) f(acc.Nick, groupName, ses.ClientId) }
// Info return usage info for a group func Info(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { if err := context.IsGroupAdmin(); err != nil { return response.NewBadRequest(err) } group, err := modelhelper.GetGroup(context.GroupName) if err != nil { return response.NewBadRequest(err) } return response.HandleResultAndError( payment.EnsureInfoForGroup(group, context.Client.Account.Nick), ) }
func TestCustomer(t *testing.T) { Convey("Given a user", t, func() { withTestServer(t, func(endpoint string) { withStubData(endpoint, func(username, groupName, sessionID string) { Convey("Then Group should have customer id", func() { group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) So(group.Payment.Customer.ID, ShouldNotBeBlank) Convey("We should be able to get the customer", func() { getURL := endpoint + EndpointCustomerGet res, err := rest.DoRequestWithAuth("GET", getURL, nil, sessionID) So(err, ShouldBeNil) So(res, ShouldNotBeNil) v := &stripe.Customer{} err = json.Unmarshal(res, v) So(err, ShouldBeNil) So(v.Deleted, ShouldEqual, false) So(v.Desc, ShouldContainSubstring, groupName) So(len(v.Meta), ShouldBeGreaterThanOrEqualTo, 2) So(v.Meta["groupName"], ShouldEqual, groupName) So(v.Meta["username"], ShouldEqual, username) Convey("After adding credit card to the user", func() { addCreditCardToUserWithChecks(endpoint, sessionID) res, err = rest.DoRequestWithAuth("GET", getURL, nil, sessionID) So(err, ShouldBeNil) So(res, ShouldNotBeNil) Convey("Customer should have CC assigned", func() { v = &stripe.Customer{} err = json.Unmarshal(res, v) So(err, ShouldBeNil) So(v.DefaultSource, ShouldNotBeNil) So(v.DefaultSource.Deleted, ShouldBeFalse) So(v.DefaultSource.ID, ShouldNotBeEmpty) }) }) }) }) }) }) }) }
func TestInvoiceHandlers(t *testing.T) { testData := ` { "id": "in_00000000000000", "object": "invoice", "amount_due": 100, "currency": "usd", "customer": %q }` tests.WithConfiguration(t, func(c *config.Config) { stripe.Key = c.Stripe.SecretToken Convey("Given stub data", t, func() { withStubData(func(username, groupName, sessionID string) { Convey("Then Group should have customer id", func() { group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) Convey("When invoice handlers are triggered", func() { var invoice *stripe.Invoice raw := fmt.Sprintf(testData, group.Payment.Customer.ID) err := json.Unmarshal([]byte(raw), &invoice) So(err, ShouldBeNil) var capturedMails []*emailsender.Mail realMailSender := mailSender mailSender = func(m *emailsender.Mail) error { capturedMails = append(capturedMails, m) return nil } eventName := "test event name" err = sendInvoiceEvent(invoice, eventName) So(err, ShouldBeNil) mailSender = realMailSender Convey("properties of event should be set accordingly", func() { So(len(capturedMails), ShouldEqual, 1) So(capturedMails[0].Subject, ShouldEqual, eventName) }) }) }) }) }) }) }
func handleInvoiceStateChange(invoice *stripe.Invoice) error { cus, err := customer.Get(invoice.Customer.ID, nil) if err != nil { return err } if err := syncGroupWithCustomerID(invoice.Customer.ID); err != nil { return err } group, err := modelhelper.GetGroup(cus.Meta["groupName"]) // we might get events from other environments where we might not have the // group in this env. if err == mgo.ErrNotFound { return nil } if err != nil { return err } status := group.Payment.Subscription.Status // if sub is in cancelled state within 2 months send an event if stripe.SubStatus(status) == SubStatusCanceled { // if group has been created in last 2 months (1 month trial + 1 month free) totalTrialTime := time.Now().UTC().Add(-time.Hour * 24 * 60) if group.Id.Time().After(totalTrialTime) { eventName := "trial ended without payment" sendEventForCustomer(invoice.Customer.ID, eventName, nil) } } // send instance notification to group go realtimehelper.NotifyGroup( group.Slug, "payment_status_changed", map[string]string{ "oldStatus": string(group.Payment.Subscription.Status), "newStatus": string(status), }, ) eventName := fmt.Sprintf("subscription status %s", status) return sendEventForCustomer(invoice.Customer.ID, eventName, nil) }
// CancelSubscriptionForGroup cancels the subscription for a team. In order to // achive that, first deletes the current subscription then subscribes to new // plan with new quantity, ( reasoning behind that is subscribing to a new plan // charges immediately ) Then deletes the current subscription again. All these // reqiured because we charge our users at the end of the month based on the // usage. So while cancelling group subscription, charge for the due usage // amount immediately then cancel subscription func CancelSubscriptionForGroup(groupName string) (*stripe.Sub, error) { group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } info, err := GetInfoForGroup(group) if err != nil { return nil, err } if err := switchToNewSub(info); err != nil { return nil, err } return DeleteSubscriptionForGroup(groupName) }
func syncGroupWithCustomerID(cusID string) error { cus, err := customer.Get(cusID, nil) if err != nil { return err } group, err := modelhelper.GetGroup(cus.Meta["groupName"]) if err == mgo.ErrNotFound { return nil } if err != nil { return err } // here sub count might be 0, but should not be gt 1 if cus.Subs.Count > 1 { return errors.New("customer should only have one subscription") } subID := "" subStatus := SubStatusCanceled // if we dont have any sub, set it as canceled if cus.Subs.Count == 1 { subID = cus.Subs.Values[0].ID subStatus = cus.Subs.Values[0].Status } // if subID and subStatus are same, update not needed if group.Payment.Subscription.ID == subID && stripe.SubStatus(group.Payment.Subscription.Status) == subStatus { return nil } return modelhelper.UpdateGroupPartial( modelhelper.Selector{"_id": group.Id}, modelhelper.Selector{ "$set": modelhelper.Selector{ "payment.subscription.id": subID, "payment.subscription.status": string(subStatus), }, }, ) }
func withStubData(f func(username string, groupName string, sessionID string)) { acc, _, groupName := models.CreateRandomGroupDataWithChecks() group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) err = modelhelper.MakeAdmin(bson.ObjectIdHex(acc.OldId), group.Id) So(err, ShouldBeNil) ses, err := modelhelper.FetchOrCreateSession(acc.Nick, groupName) tests.ResultedWithNoErrorCheck(ses, err) cus, err := EnsureCustomerForGroup(acc.Nick, groupName, &stripe.CustomerParams{}) tests.ResultedWithNoErrorCheck(cus, err) f(acc.Nick, groupName, ses.ClientId) err = DeleteCustomerForGroup(groupName) So(err, ShouldBeNil) }
func TestCheckCustomerHasSource(t *testing.T) { tests.WithConfiguration(t, func(c *config.Config) { stripe.Key = c.Stripe.SecretToken Convey("Given a customer", t, func() { withStubData(func(username, groupName, sessionID string) { group, err := modelhelper.GetGroup(groupName) tests.ResultedWithNoErrorCheck(group, err) So(group.Payment.Customer.ID, ShouldNotBeBlank) Convey("When customer does not have card", func() { err := CheckCustomerHasSource(group.Payment.Customer.ID) Convey("CheckCustomerHasSource should return false", func() { So(err, ShouldEqual, ErrCustomerSourceNotExists) Convey("When customer does have card", func() { // add credit card to the user withTestCreditCardToken(func(token string) { // attach payment source cp := &stripe.CustomerParams{ Source: &stripe.SourceParams{ Token: token, }, } c, err := UpdateCustomerForGroup(username, groupName, cp) tests.ResultedWithNoErrorCheck(c, err) Convey("CheckCustomerHasSource should return true", func() { err := CheckCustomerHasSource(group.Payment.Customer.ID) So(err, ShouldBeNil) }) }) }) }) }) }) }) }) }
// DeleteCustomerForGroup deletes the customer for a given group. If customer is // not registered, returns error. If customer is already deleted, returns success. // Not necessarily should be used. Call with care. func DeleteCustomerForGroup(groupName string) error { group, err := modelhelper.GetGroup(groupName) if err != nil { return err } if group.Payment.Customer.ID == "" { return nil } if err := deleteCustomer(group.Payment.Customer.ID); err != nil { return err } return modelhelper.UpdateGroupPartial( modelhelper.Selector{"_id": group.Id}, modelhelper.Selector{ // deleting customer deletes everything belong to that customer in stripe, // so say we all "$unset": modelhelper.Selector{"payment": ""}, }, ) }