Skip to content

Commit

Permalink
automated donation processing
Browse files Browse the repository at this point in the history
  • Loading branch information
leijurv committed Feb 11, 2020
1 parent d506474 commit 9efdaf7
Show file tree
Hide file tree
Showing 14 changed files with 461 additions and 41 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ require (
github.com/plutov/paypal/v3 v3.0.11
github.com/stretchr/testify v1.4.0
github.com/valyala/fasttemplate v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc // indirect
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb // indirect
golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f // indirect
golang.org/x/text v0.3.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsu
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678 h1:wCWoJcFExDgyYx2m2hpHgwz8W3+FPdfldvIgzqDIhyg=
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
Expand Down
35 changes: 21 additions & 14 deletions src/api/v1/donate.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package v1

import (
"github.com/ImpactDevelopment/ImpactServer/src/jwt"
"log"
"net/http"

"github.com/ImpactDevelopment/ImpactServer/src/database"
"github.com/ImpactDevelopment/ImpactServer/src/paypal"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"net/http"
)

type (
donationRequest struct {
ID string `json:"orderID" form:"orderID" query:"orderID"`
}
donationResponse struct {
Amount int `json:"amount"`
Token string `json:"token,omitempty"`
Token string `json:"token"`
}
)

Expand Down Expand Up @@ -42,18 +44,23 @@ func afterDonation(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "Error validating order").SetInternal(err)
}

// This token can be used to register for an impact account, assuming amount is high enough
token := jwt.CreateDonationJWT(order)
if token == "" {
return echo.NewHTTPError(http.StatusInternalServerError, "Error creating donation token")
var token uuid.UUID
err = database.DB.QueryRow("INSERT INTO pending_donations(paypal_order_id, amount) VALUES ($1, $2) RETURNING token", order.ID, order.Total()).Scan(&token)
if err != nil {
log.Println(err)
return echo.NewHTTPError(http.StatusInternalServerError, "Error saving pending donation").SetInternal(err)
}

// TODO instead of returning a token, should we store it in the database and return a short token id instead?
// The jwt is rather long if users are planning to share it as a gift...
// Another option would be to compress it somehow maybe 🤔
if order.Total() < 500 {
// if the donation is too small, save it
// maybe they make multiple that sum up to over 500 eventually, lets make a record of it idk
// just dont give em a registration token lol
log.Println("Donation with PayPal order ID", order.ID, "and total", order.Total(), "is too small. token would have been", token)
return c.JSON(http.StatusOK, donationResponse{
Token: "thanks",
})
}
return c.JSON(http.StatusOK, donationResponse{
Amount: order.Total(),
Token: token,
// TODO return expiry too?
Token: token.String(),
})
}
94 changes: 94 additions & 0 deletions src/api/v1/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package v1

import (
"log"
"net/http"
"strings"

"github.com/ImpactDevelopment/ImpactServer/src/database"
"github.com/ImpactDevelopment/ImpactServer/src/discord"
"github.com/ImpactDevelopment/ImpactServer/src/util"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt"
)

type registrationCheck struct {
Token string `json:"token" form:"token" query:"token"`
}

type registration struct {
Token string `json:"token" form:"token" query:"token"`
Discord string `json:"discord" form:"discord" query:"discord"`
Mcuuid string `json:"mcuuid" form:"mcuuid" query:"mcuuid"`
Email string `json:"email" form:"email" query:"email"`
Password string `json:"password" form:"password" query:"password"`
}

func checkToken(c echo.Context) error {
body := &registrationCheck{}
err := c.Bind(body)
if err != nil {
return err
}
if body.Token == "" {
return echo.NewHTTPError(http.StatusBadRequest, "token missing")
}
var createdAt int64
err = database.DB.QueryRow("SELECT created_at FROM pending_donations WHERE token = $1 AND NOT used", body.Token).Scan(&createdAt)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid token")
}
return c.String(200, "true")
}

func registerWithToken(c echo.Context) error {
body := &registration{}
err := c.Bind(body)
if err != nil {
return err
}
if body.Token == "" || body.Discord == "" || body.Mcuuid == "" || body.Email == "" || body.Password == "" {
return echo.NewHTTPError(http.StatusBadRequest, "empty field(s)")
}
var createdAt int64
err = database.DB.QueryRow("SELECT created_at FROM pending_donations WHERE token = $1 AND NOT used", body.Token).Scan(&createdAt)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid token")
}
log.Println(body)
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
if !discord.CheckServerMembership(body.Discord) {
return echo.NewHTTPError(http.StatusBadRequest, "join our discord first lol")
}

req, err := util.GetRequest("https://api.mojang.com/user/profiles/" + strings.Replace(body.Mcuuid, "-", "", -1) + "/names")
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "bad mc uuid")
}
resp, err := req.Do()
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "bad mc uuid")
}
if resp.Code() != 200 {
return echo.NewHTTPError(http.StatusBadRequest, "bad mc uuid")
}
_, err = database.DB.Exec("UPDATE pending_donations SET used = true WHERE token = $1", body.Token)
if err != nil {
log.Println(err)
return err
}
_, err = database.DB.Exec("INSERT INTO users(email, password_hash, mc_uuid, discord_id) VALUES ($1, $2, $3, $4)", body.Email, hashedPassword, body.Mcuuid, body.Discord)
if err != nil {
log.Println(err)
return err
}
err = discord.GiveDonator(body.Discord)
if err != nil {
log.Println(err)
return err
}
return c.String(200, "SUCCESS")
}
5 changes: 4 additions & 1 deletion src/api/v1/server.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package v1

import (
"github.com/ImpactDevelopment/ImpactServer/src/jwt"
"net/http"

"github.com/ImpactDevelopment/ImpactServer/src/jwt"

"github.com/ImpactDevelopment/ImpactServer/src/middleware"
"github.com/labstack/echo/v4"
)
Expand All @@ -22,6 +23,8 @@ func API(api *echo.Group) {
api.Match([]string{http.MethodGet, http.MethodPost}, "/login/minecraft", jwt.MinecraftLoginHandler, middleware.NoCache())
api.Match([]string{http.MethodGet, http.MethodPost}, "/login/discord", jwt.DiscordLoginHandler, middleware.NoCache())
api.Match([]string{http.MethodGet, http.MethodPost}, "/paypal/afterpayment", afterDonation, middleware.NoCache())
api.Match([]string{http.MethodGet, http.MethodPost}, "/checktoken", checkToken, middleware.NoCache())
api.Match([]string{http.MethodGet, http.MethodPost}, "/register/token", registerWithToken, middleware.NoCache())
api.GET("/emailtest", emailTest, middleware.NoCache())
api.GET("/premiumcheck", premiumCheck, middleware.NoCache())
api.GET("/integration/futureclient/masonlist", futureIntegration, middleware.NoCache())
Expand Down
15 changes: 15 additions & 0 deletions src/database/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ func createTables() error {
return err
}

_, err = DB.Exec(`
CREATE TABLE IF NOT EXISTS pending_donations (
token UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
created_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT, -- unix seconds
paypal_order_id TEXT, -- can be null in case we want to make a "gift card" with no paypal order id attached
amount INTEGER, -- can be null for the same reason
used BOOL NOT NULL DEFAULT FALSE
);
`)
if err != nil {
log.Println("Unable to create pending_donations table")
return err
}

_, err = DB.Exec(`
CREATE TABLE IF NOT EXISTS users (
user_id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
Expand Down
48 changes: 48 additions & 0 deletions src/discord/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package discord

import (
"fmt"
"os"

"github.com/bwmarrin/discordgo"
)

var discord *discordgo.Session

var guildID string
var donatorRole string

func init() {
token := os.Getenv("DISCORD_BOT_TOKEN")
if token == "" {
fmt.Println("WARNING: No discord bot token, will not be able to grant donator role!")
return
}
guildID = os.Getenv("DISCORD_GUILD_ID")
donatorRole = os.Getenv("DISCORD_DONATOR_ROLE_ID")
if guildID == "" || donatorRole == "" {
fmt.Println("WARNING: Discord info is bad")
return
}
var err error
discord, err = discordgo.New("Bot " + token)
if err != nil {
panic(err)
}
user, err := discord.User("@me")
if err != nil {
panic(err)
}

myselfID := user.ID
fmt.Println("I am", myselfID)
}

func GiveDonator(discordID string) error {
return discord.GuildMemberRoleAdd(guildID, discordID, donatorRole)
}

func CheckServerMembership(discordID string) bool {
member, err := discord.GuildMember(guildID, discordID)
return err == nil && member != nil
}
23 changes: 2 additions & 21 deletions src/jwt/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package jwt
import (
"crypto/rsa"
"fmt"
"github.com/ImpactDevelopment/ImpactServer/src/paypal"
"github.com/labstack/echo/v4"
"net/http"
"os"
"time"

"github.com/labstack/echo/v4"

"github.com/ImpactDevelopment/ImpactServer/src/users"
"github.com/ImpactDevelopment/ImpactServer/src/util"
"github.com/gbrlsnchs/jwt/v3"
Expand Down Expand Up @@ -58,25 +58,6 @@ func init() {
rs512 = jwt.NewRS512(jwt.RSAPrivateKey(key), jwt.RSAPublicKey(&key.PublicKey))
}

// CreateDonationJWT returns a jwt token for a paypal order which can then be used
// to register for an Impact Account.
func CreateDonationJWT(order *paypal.Order) string {
now := time.Now()

return createJWT(donationJWT{
Payload: jwt.Payload{
Issuer: jwtIssuerURL,
Subject: "",
Audience: jwt.Audience{"impact_account"},
ExpirationTime: jwt.NumericDate(now.Add(90 * 24 * time.Hour)),
NotBefore: jwt.NumericDate(now),
IssuedAt: jwt.NumericDate(now),
},
OrderID: order.ID,
Amount: order.Total(),
})
}

// CreateUserJWT returns a jwt token for the user with the subject (if not empty).
// The client can then use this to verify that the user has authenticated
// with a valid Impact server by checking the signature and issuer.
Expand Down
3 changes: 2 additions & 1 deletion src/jwt/minecraft.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package jwt

import (
"net/http"

"github.com/ImpactDevelopment/ImpactServer/src/database"
"github.com/ImpactDevelopment/ImpactServer/src/util"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"net/http"
)

type minecraftRequest struct {
Expand Down
3 changes: 2 additions & 1 deletion src/paypal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package paypal

import (
"fmt"
"github.com/plutov/paypal/v3"
"os"

"github.com/plutov/paypal/v3"
)

var client *paypal.Client
Expand Down
3 changes: 2 additions & 1 deletion src/paypal/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package paypal
import (
"errors"
"fmt"
"github.com/plutov/paypal/v3"
"strconv"
"strings"

"github.com/plutov/paypal/v3"
)

type Order struct {
Expand Down
2 changes: 1 addition & 1 deletion src/users/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

type User struct {
ID uuid.UUID
ID uuid.UUID
Email string
MinecraftID *uuid.UUID
DiscordID string
Expand Down
Loading

0 comments on commit 9efdaf7

Please sign in to comment.