📜 ⬆️ ⬇️

Ecosystem support. Automate user registration with Golang

What are we doing?


Our team is developing a software platform for sending notifications via REST API to mobile devices. These are currently push notifications for iOS devices, as well as SMS due to integration with Twilio ). In order to organize the support process, as well as the user community, a number of information systems and services was selected (see list below), which, from our point of view, made it possible to solve the problem in the shortest possible time and with minimal effort.


Tasks


Automate the following:



Systems used (and their purpose)


All of the following information systems and services have a REST API interface that allows you to solve tasks.



Process description


In our case, the following events are exhaustive:


  1. When downloading a software product, you need to create a user in the accounting system for users, as well as send an invitation to the community portal
  2. When activating commercial support, you need to add the user to the appropriate group of the user circulation system
  3. When the commercial subscription expires, you must remove the user from the appropriate group.

Implementation


First of all, the source of all the above events of the process is the Paddle platform, namely their Events / Alerts subsystem. When a certain event occurs, Paddle initiates a POST request to a predetermined URL (of our server).
Events that we will handle:



The request from the side of Paddle contains the following information (the content of the request from the side of Paddle may differ depending on the events described above, but the elements of interest remain the same in any case):


{ "method": "POST", "data": { "alert_id": "XXXXXX", "alert_name": "subscription_created", "cancel_url": "https://checkout.paddle.com/subscription/cancel?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "currency": "USD", "email": "name@example.com", "event_time": "2016-08-05 13:26:06", "next_bill_date": "2017-08-05", "passthrough": "", "status": "active", "subscription_id": "XXXXXX", "subscription_plan_id": "XXXXXX", "update_url": "https://checkout.paddle.com/subscription/update?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "p_signature": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } } 

and expects HTTP 200 OK in response:


 { "http_code": 200, "redirect_url": "", "content_type": "text/plain; charset=utf-8", "total_time": 1.018837 } 

In case the answer differs from HTTP 200 OK , Paddle will send a repeated request towards our server for 72 hours with an interval of once per hour.


In the above request from Paddle, we are interested in the alert_name and email parameters that we will use to register users in our information systems.


Parse requests from Paddle


 type Event struct { AlertName string `form:"alert_name" binding:"required"` Email string `form:"email" binding:"required"` Status string `form:"status"` SubscriptionId int `form:"subscription_id"` OrderId string `form:"order_id"` SubscriptionPlanId int `form:"subscription_plan_id"` Country string `form:"country"` Fee float32 `form:"fee"` Currency string `form:"currency"` Psignature string `form:"p_signature"` Passthrough string `form:"passthrough"` PaymentMethod string `form:"payment_method"` PaymentTax float32 `form:"payment_tax"` SaleGross float32 `form:"sale_gross"` Earnings float32 `form:"earnings"` EventTime string `form:"event_time"` NextBillDate string `form:"next_bill_date"` } func (e *Event) ToFields() (fields log.Fields) { fields = make(log.Fields) fields["alert_name"] = e.AlertName fields["email"] = e.Email fields["status"] = e.Status fields["subscription_id"] = e.SubscriptionId fields["order_id"] = e.OrderId fields["subscription_plan_id"] = e.SubscriptionPlanId fields["country"] = e.Country fields["fee"] = e.Fee fields["currency"] = e.Currency fields["p_signature"] = e.Psignature fields["passthrough"] = e.Passthrough fields["payment_method"] = e.PaymentMethod fields["payment_tax"] = e.PaymentTax fields["sale_gross"] = e.SaleGross fields["earnings"] = e.Earnings fields["event_time"] = e.EventTime fields["next_bill_date"] = e.NextBillDate return } 

We perform request routing depending on the event


 func eventPOST(c *gin.Context) { status := []bool{} c.Bind(&event) log.WithFields(event.ToFields()).Info("Processing event") switch event.AlertName { case "subscription_created": status = append(status, InviteToJira(event.Email)) status = append(status, AddToJiraGroup(event.Email)) status = append(status, InviteToSlack(event.Email)) case "payment_succeeded": status = append(status, InviteToJira(event.Email)) status = append(status, InviteToSlack(event.Email)) case "subscription_cancelled": status = append(status, RemoveFromJiraGroup(event.Email)) default: status = append(status, false) log.WithFields(log.Fields{ "event": event.AlertName, }).Error("Unknown event") } for _, s := range status { if s == false { c.String(http.StatusInternalServerError, "not Ok\n") return } } c.String(http.StatusOK, "Ok\n") return } 

Further, depending on the received event, we will process them accordingly.


Inviting a user to Slack (community portal)


 func InviteToSlack(username string) bool { api := slack.New(Conf.Slack.Token) err := api.InviteToTeam(Conf.Slack.Team, "", "", username) if err != nil { if strings.Contains(err.Error(), "already_in_team") { log.WithFields(log.Fields{ "user": username, "slack_team": Conf.Slack.Team, }).Warn("User already in Slack team") return true } else if strings.Contains(err.Error(), "already_invited") { log.WithFields(log.Fields{ "user": username, "slack_team": Conf.Slack.Team, }).Warn("User already invited to Slack") return true } log.Error(err) return false } log.WithFields(log.Fields{ "user": username, }).Warn("User successfuly invited to Slack") return true } 

Inviting / registering a user to Jira (a system of recording appeals from users)


 func InviteToJira(username string) bool { jira := jira.New(Conf.Jira.Host, Conf.Jira.User, Conf.Jira.Password, false) _, err := jira.GetUser(username) if err != nil { if err.Error() == "Not Found" { ok, err := jira.InviteSdUser(username, Conf.Jira.Invite_uri) if ok { log.WithFields(log.Fields{ "user": username, }).Info("Invited user to JIRA") return true } else { log.WithFields(log.Fields{ "user": username, "error": err, }).Error("Invite to JIRA Failed") return false } } else { log.Error(err) return false } } log.WithFields(log.Fields{ "user": username, }).Warn("User already Invited to JIRA") return true } 

Add user to Jira group (commercial support)


 func AddToJiraGroup(username string) bool { jira := jira.New(Conf.Jira.Host, Conf.Jira.User, Conf.Jira.Password, false) ok, err := jira.AddToGroup(username, Conf.Jira.Invite_group) if ok { log.WithFields(log.Fields{ "user": username, "group": Conf.Jira.Invite_group, }).Info("Added user to Jira Group") return true } else { log.WithFields(log.Fields{ "user": username, "group": Conf.Jira.Invite_group, "error": err, }).Error("Failed to add user to Jira Group") return false } } 

Remove user from Jira group (commercial support)


 func RemoveFromJiraGroup(username string) bool { jira := jira.New(Conf.Jira.Host, Conf.Jira.User, Conf.Jira.Password, false) ok, err := jira.RemoveFromGroup(username, Conf.Jira.Invite_group) if ok { log.WithFields(log.Fields{ "user": username, "group": Conf.Jira.Invite_group, }).Info("Removed user from Jira Group") return true } else { log.WithFields(log.Fields{ "user": username, "group": Conf.Jira.Invite_group, "error": err, }).Error("Failed to remove User from Jira Group") return false } } 

Used modules (main)



Conclusion


The source code for the solution described above is available on GitHub: paddle-endpoint .


Thank you for attention!


')

Source: https://habr.com/ru/post/307344/


All Articles