-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce stripe integration for 111 NFT (#147)
* Introduce stripe integration for 111 NFT * Remove unused env vars * Integrate delegate mint * Create payment intent with usd equivalent to 11.1 aptos * `stripe-webhook` -> `payment-webhook` `111-nft` -> `erebrus` `Buy111NFTResponse` -> `BuyErebrusNFTResponse` * Support for multiple subscriptions * format: envconfig.go
- Loading branch information
1 parent
baacbc9
commit 1b7b32a
Showing
15 changed files
with
275 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package subscription | ||
|
||
import ( | ||
"math" | ||
"net/http" | ||
|
||
"github.com/NetSepio/gateway/api/middleware/auth/paseto" | ||
"github.com/NetSepio/gateway/config/dbconfig" | ||
"github.com/NetSepio/gateway/models" | ||
"github.com/NetSepio/gateway/util/pkg/aptos" | ||
"github.com/NetSepio/gateway/util/pkg/logwrapper" | ||
"github.com/TheLazarusNetwork/go-helpers/httpo" | ||
"github.com/gin-gonic/gin" | ||
"github.com/google/uuid" | ||
"github.com/stripe/stripe-go/v76" | ||
"github.com/stripe/stripe-go/v76/paymentintent" | ||
) | ||
|
||
func Buy111NFT(c *gin.Context) { | ||
db := dbconfig.GetDb() | ||
userId := c.GetString(paseto.CTX_USER_ID) | ||
walletAddress := c.GetString(paseto.CTX_WALLET_ADDRES) | ||
if walletAddress == "" { | ||
logwrapper.Errorf("user has no wallet address") | ||
httpo.NewErrorResponse(http.StatusBadRequest, "user doesn't have any wallet linked").SendD(c) | ||
return | ||
} | ||
|
||
coinPrice, err := aptos.GetCoinPrice() | ||
if err != nil { | ||
logwrapper.Errorf("failed to get coin price: %v", err) | ||
httpo.NewErrorResponse(http.StatusInternalServerError, "internal server error").SendD(c) | ||
return | ||
} | ||
params := &stripe.PaymentIntentParams{ | ||
Amount: stripe.Int64(int64(math.Ceil(coinPrice * 11.1 * 100))), | ||
Currency: stripe.String(string(stripe.CurrencyUSD)), | ||
// In the latest version of the API, specifying the `automatic_payment_methods` parameter is optional because Stripe enables its functionality by default. | ||
AutomaticPaymentMethods: &stripe.PaymentIntentAutomaticPaymentMethodsParams{ | ||
Enabled: stripe.Bool(true), | ||
}, | ||
} | ||
|
||
pi, err := paymentintent.New(params) | ||
if err != nil { | ||
logwrapper.Errorf("failed to create session: %v", err) | ||
httpo.NewErrorResponse(http.StatusInternalServerError, "internal server error").SendD(c) | ||
return | ||
} | ||
|
||
// type UsersStripePi struct { | ||
// Id string `gorm:"primary_key" json:"id,omitempty"` | ||
// UserId string `json:"userId,omitempty"` | ||
// StripePiId string `json:"stripePiId,omitempty"` | ||
// StripePiType string `json:"stripePiType,omitempty"` | ||
// } | ||
|
||
// insert in above table | ||
err = db.Create(&models.UserStripePi{ | ||
Id: uuid.NewString(), | ||
UserId: userId, | ||
StripePiId: pi.ID, | ||
StripePiType: models.Erebrus111NFT, | ||
}).Error | ||
if err != nil { | ||
logwrapper.Errorf("failed to insert into users_stripe_pi: %v", err) | ||
httpo.NewErrorResponse(http.StatusInternalServerError, "internal server error").SendD(c) | ||
return | ||
} | ||
|
||
httpo.NewSuccessResponseP(http.StatusOK, "payment intent created", BuyErebrusNFTResponse{ClientSecret: pi.ClientSecret}).SendD(c) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package subscription | ||
|
||
import ( | ||
"github.com/NetSepio/gateway/api/middleware/auth/paseto" | ||
"github.com/gin-gonic/gin" | ||
) | ||
|
||
// ApplyRoutes applies router to gin Router | ||
func ApplyRoutes(r *gin.RouterGroup) { | ||
g := r.Group("/subscription") | ||
{ | ||
g.POST("payment-webhook", StripeWebhookHandler) | ||
g.Use(paseto.PASETO(false)) | ||
g.POST("erebrus", Buy111NFT) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package subscription | ||
|
||
type BuyErebrusNFTResponse struct { | ||
ClientSecret string `json:"clientSecret"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package subscription | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/NetSepio/gateway/config/dbconfig" | ||
"github.com/NetSepio/gateway/config/envconfig" | ||
"github.com/NetSepio/gateway/models" | ||
"github.com/NetSepio/gateway/util/pkg/aptos" | ||
"github.com/NetSepio/gateway/util/pkg/logwrapper" | ||
"github.com/gin-gonic/gin" | ||
"github.com/stripe/stripe-go/v76" | ||
"github.com/stripe/stripe-go/v76/webhook" | ||
"gorm.io/gorm" | ||
) | ||
|
||
func StripeWebhookHandler(c *gin.Context) { | ||
db := dbconfig.GetDb() | ||
|
||
const MaxBodyBytes = int64(65536) | ||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, MaxBodyBytes) | ||
payload, err := io.ReadAll(c.Request.Body) | ||
if err != nil { | ||
logwrapper.Errorf("Error reading request body: %v", err) | ||
c.Status(http.StatusServiceUnavailable) | ||
return | ||
} | ||
|
||
event, err := webhook.ConstructEvent(payload, c.GetHeader("Stripe-Signature"), envconfig.EnvVars.STRIPE_WEBHOOK_SECRET) | ||
if err != nil { | ||
logwrapper.Errorf("Error verifying webhook signature: %v", err) | ||
c.Status(http.StatusBadRequest) | ||
return | ||
} | ||
switch event.Type { | ||
case stripe.EventTypePaymentIntentSucceeded: | ||
var paymentIntent stripe.PaymentIntent | ||
err := json.Unmarshal(event.Data.Raw, &paymentIntent) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) | ||
c.Status(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// get user with stripe_pi_id | ||
var userStripePi models.UserStripePi | ||
if err := db.Where("stripe_pi_id = ?", paymentIntent.ID).First(&userStripePi).Error; err != nil { | ||
if errors.Is(err, gorm.ErrRecordNotFound) { | ||
//warn and return success | ||
logwrapper.Warnf("No user found with stripe_pi_id: %v", err) | ||
c.JSON(http.StatusOK, gin.H{"status": "received"}) | ||
return | ||
} | ||
logwrapper.Errorf("Error getting user with stripe_pi_id: %v", err) | ||
c.Status(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// get user with user_id | ||
var user models.User | ||
if err := db.Where("user_id = ?", userStripePi.UserId).First(&user).Error; err != nil { | ||
logwrapper.Errorf("Error getting user with user_id: %v", err) | ||
c.Status(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
if _, err = aptos.DelegateMintNft(*user.WalletAddress); err != nil { | ||
logwrapper.Errorf("Error minting nft: %v", err) | ||
c.Status(http.StatusInternalServerError) | ||
return | ||
} | ||
fmt.Println("minting nft -- 111NFT") | ||
|
||
case stripe.EventTypePaymentIntentCanceled: | ||
err := HandleCanceledOrFailedPaymentIntent(event.Data.Raw) | ||
if err != nil { | ||
logwrapper.Errorf("Error handling canceled payment intent: %v", err) | ||
c.Status(http.StatusInternalServerError) | ||
return | ||
} | ||
c.JSON(http.StatusOK, gin.H{"status": "received"}) | ||
} | ||
|
||
c.JSON(http.StatusOK, gin.H{"status": "received"}) | ||
} | ||
|
||
func HandleCanceledOrFailedPaymentIntent(eventDataRaw json.RawMessage) error { | ||
var paymentIntent stripe.PaymentIntent | ||
err := json.Unmarshal(eventDataRaw, &paymentIntent) | ||
if err != nil { | ||
return fmt.Errorf("error parsing webhook JSON: %w", err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
CREATE TABLE user_stripe_pis ( | ||
id UUID PRIMARY KEY, | ||
user_id UUID REFERENCES users(user_id), | ||
stripe_pi_id TEXT UNIQUE, | ||
stripe_pi_type TEXT, | ||
created_at timestamp with time zone DEFAULT current_timestamp | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package aptos | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
) | ||
|
||
type CoinGeckoResponse struct { | ||
Aptos struct { | ||
USD float64 `json:"usd"` | ||
} `json:"aptos"` | ||
} | ||
|
||
func GetCoinPrice() (float64, error) { | ||
url := "https://api.coingecko.com/api/v3/simple/price?ids=aptos&vs_currencies=usd" | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return 0, fmt.Errorf("error fetching data: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
var result CoinGeckoResponse | ||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { | ||
return 0, fmt.Errorf("error decoding JSON: %w", err) | ||
} | ||
|
||
return result.Aptos.USD, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters