Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
emmdim committed May 16, 2024
1 parent b1e022b commit 819f394
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 9 deletions.
12 changes: 8 additions & 4 deletions faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ import (
)

type faucet struct {
signer *ethereum.SignKeys
authTypes map[string]uint64
waitPeriod time.Duration
storage *storage
signer *ethereum.SignKeys
authTypes map[string]uint64
waitPeriod time.Duration
storage *storage
stripeKey string
stripePriceId string
stripeWebhookSecret string
domain string
}

// prepareFaucetPackage prepares a faucet package, including the signature, for the given address.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/ethereum/go-ethereum v1.13.4
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.16.0
github.com/stripe/stripe-go/v78 v78.3.0
go.vocdoni.io/dvote v1.10.0
gopkg.in/yaml.v3 v3.0.1
)
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v78 v78.3.0 h1:FYlKhJKZdZ/1vATbuIN4T107DeL7w9oV13IcPOEwyPQ=
github.com/stripe/stripe-go/v78 v78.3.0/go.mod h1:GjncxVLUc1xoIOidFqVwq+y3pYiG7JLVWiVQxTsLrvQ=
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
Expand Down Expand Up @@ -1801,6 +1803,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
Expand Down
146 changes: 146 additions & 0 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/ethereum/go-ethereum/common"
"github.com/vocdoni/vocfaucet/aragondaohandler"
Expand All @@ -11,6 +13,10 @@ import (
"go.vocdoni.io/dvote/httprouter/apirest"
"go.vocdoni.io/dvote/log"
"go.vocdoni.io/dvote/types"

"github.com/stripe/stripe-go/v78"
"github.com/stripe/stripe-go/v78/checkout/session"
"github.com/stripe/stripe-go/v78/webhook"
)

// Register the handlers URLs
Expand All @@ -24,6 +30,33 @@ func (f *faucet) registerHandlers(api *apirest.API) {
log.Fatal(err)
}

if err := api.RegisterMethod(
"/create-checkout-session",
"POST",
apirest.MethodAccessTypePublic,
f.createCheckoutSession,
); err != nil {
log.Fatal(err)
}

if err := api.RegisterMethod(
"/session-status",
"GET",
apirest.MethodAccessTypePublic,
f.retrieveCheckoutSession,
); err != nil {
log.Fatal(err)
}

if err := api.RegisterMethod(
"/webhook",
"POST",
apirest.MethodAccessTypePublic,
f.handleWebhook,
); err != nil {
log.Fatal(err)
}

if f.authTypes[AuthTypeOpen] > 0 {
if err := api.RegisterMethod(
"/open/claim/{to}",
Expand Down Expand Up @@ -67,6 +100,119 @@ func (f *faucet) registerHandlers(api *apirest.API) {
}
}

// createCheckoutSession creates a new Stripe Checkout session
func (f *faucet) createCheckoutSession(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error {
stripe.Key = f.stripeKey
params := &stripe.CheckoutSessionParams{
UIMode: stripe.String("embedded"),
ReturnURL: stripe.String(f.domain + "/return.html?session_id={CHECKOUT_SESSION_ID}"),
LineItems: []*stripe.CheckoutSessionLineItemParams{
&stripe.CheckoutSessionLineItemParams{
// Provide the exact Price ID (for example, pr_1234) of the product you want to sell
Price: stripe.String(f.stripePriceId),
Quantity: stripe.Int64(1),
},
},
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
}

s, err := session.New(params)

if err != nil {
errReason := fmt.Sprintf("session.New: %v", err)
return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), CodeErrProviderError)
//
}

data := &struct {
ClientSecret string `json:"clientSecret"`
}{
ClientSecret: s.ClientSecret,
}
return ctx.Send(new(HandlerResponse).Set(data).MustMarshall(), apirest.HTTPstatusOK)
}

func (f *faucet) retrieveCheckoutSession(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error {
// s, _ := session.Get(r.URL.Query().Get("session_id"), nil)
stripe.Key = f.stripeKey
s, err := session.Get((ctx.URLParam("session_id")), nil)
if err != nil {
return err
}

data := &struct {
Status string `json:"status"`
CustomerEmail string `json:"customer_email"`
}{
Status: string(s.Status),
CustomerEmail: string(s.CustomerDetails.Email),
}
return ctx.Send(new(HandlerResponse).Set(data).MustMarshall(), apirest.HTTPstatusOK)
}

func (f *faucet) handleWebhook(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error {
const MaxBodyBytes = int64(65536)
maxBytesBody := http.MaxBytesReader(ctx.Writer, ctx.Request.Body, MaxBodyBytes)
body, err := ioutil.ReadAll(maxBytesBody)
if err != nil {
errReason := fmt.Sprintf("Error reading request body: %v\n", err)
return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), http.StatusServiceUnavailable)
}
// Pass the request body and Stripe-Signature header to ConstructEvent, along with the webhook signing key
// You can find your endpoint's secret in your webhook settings
event, err := webhook.ConstructEvent(body, ctx.Request.Header.Get("Stripe-Signature"), f.stripeWebhookSecret)

if err != nil {
errReason := fmt.Sprintf("Error verifying webhook signature: %v\n", err)
return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), http.StatusBadRequest)
}

// Handle the checkout.session.completed event
if event.Type == "checkout.session.completed" {
var session stripe.CheckoutSession
err := json.Unmarshal(event.Data.Raw, &session)
if err != nil {
errReason := fmt.Sprintf("Error parsing webhook JSON: %v\n", err)
return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), http.StatusBadRequest)
}

params := &stripe.CheckoutSessionParams{}
params.AddExpand("line_items")

// Retrieve the session. If you require line items in the response, you may include them by expanding line_items.
sessionWithLineItems, _ := session.Get(session.ID, params)
lineItems := sessionWithLineItems.LineItems
// Fulfill the purchase...
// TODO recover number of tokens and address
// use https://stripe.com/docs/api/metadata
return processPaymentTransfer(ctx, lineItems)
}

return ctx.Send([]byte("success"), http.StatusOK)
}

func (f *faucet) processPaymentTransfer(ctx *httprouter.HTTPContext, amount uint64) error {
if amount == 0 {
return ctx.Send(new(HandlerResponse).SetError(ReasonErrUnsupportedAuthType).MustMarshall(), CodeErrUnsupportedAuthType)
}
addr, err := stringToAddress(ctx.URLParam("to"))
if err != nil {
return err
}
// if funded, t := f.storage.checkIsFundedUserID(addr.Bytes(), AuthTypeOpen); funded {
// errReason := fmt.Sprintf("address %s already funded, wait until %s", addr.Hex(), t)
// return ctx.Send(new(HandlerResponse).SetError(errReason).MustMarshall(), CodeErrFlood)
// }
data, err := f.prepareFaucetPackage(addr, AuthTypeOpen)
if err != nil {
return err
}
if err := f.storage.addFundedUserID(addr.Bytes(), AuthTypeOpen); err != nil {
return err
}
return ctx.Send(new(HandlerResponse).Set(data).MustMarshall(), apirest.HTTPstatusOK)
}

// Returns the list of supported auth types
func (f *faucet) authTypesHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error {
data := &AuthTypes{
Expand Down
2 changes: 2 additions & 0 deletions handlers_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const (
CodeErrIncorrectParams = 408
CodeErrInternalError = 409
ReasonErrAragonDaoAddress = "could not find the signer address in any Aragon DAO"
CodeErrProviderError = 410
ReasonErrProviderError = "error obtaining the oAuthToken"
)

// HandlerResponse is the response format for the Handlers
Expand Down
29 changes: 24 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ func main() {
flag.String("amounts", "100", "tokens to send per request (comma separated), the order must match the auth types")
flag.Duration("waitPeriod", 1*time.Hour, "wait period between requests for the same user")
flag.StringP("dbType", "t", db.TypePebble, fmt.Sprintf("key-value db type [%s,%s,%s]", db.TypePebble, db.TypeLevelDB, db.TypeMongo))
flag.String("stripeKey", "", "stripe secret key")
flag.String("stripePriceId", "", "stripe price id")
flag.String("stripeWebhookSecret", "", "stripe webhook secret key")
flag.Parse()

// Setting up viper
Expand Down Expand Up @@ -86,6 +89,15 @@ func main() {
if err := viper.BindPFlag("dbType", flag.Lookup("dbType")); err != nil {
panic(err)
}
if err := viper.BindPFlag("stripeKey", flag.Lookup("stripeKey")); err != nil {
panic(err)
}
if err := viper.BindPFlag("stripePriceId", flag.Lookup("stripePriceId")); err != nil {
panic(err)
}
if err := viper.BindPFlag("stripeWebhookSecret", flag.Lookup("stripeWebhookSecret")); err != nil {
panic(err)
}

// check if config file exists
_, err := os.Stat(path.Join(dataDir, "faucet.yml"))
Expand Down Expand Up @@ -121,8 +133,12 @@ func main() {
privKey := viper.GetString("privKey")
auth := viper.GetString("auth")
amounts := viper.GetString("amounts")

waitPeriod := viper.GetDuration("waitPeriod")
dbType := viper.GetString("dbType")
stripeKey := viper.GetString("stripeKey")
stripePriceId := viper.GetString("stripePriceId")
stripeWebhookSecret := viper.GetString("stripeWebhookSecret")

// parse auth types and amounts
authNames := strings.Split(auth, ",")
Expand Down Expand Up @@ -174,13 +190,16 @@ func main() {
if err != nil {
log.Fatal(err)
}

// create the faucet instance
f := faucet{
signer: &signer,
authTypes: authTypes,
waitPeriod: waitPeriod,
storage: storage,
signer: &signer,
authTypes: authTypes,
waitPeriod: waitPeriod,
storage: storage,
stripeKey: stripeKey,
stripePriceId: stripePriceId,
stripeWebhookSecret: stripeWebhookSecret,
domain: fmt.Sprintf("http://%s:%d%s", listenHost, listenPort, baseRoute),
}

// init API
Expand Down

0 comments on commit 819f394

Please sign in to comment.