211 lines
5.4 KiB
Go
211 lines
5.4 KiB
Go
package stripe
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/stripe/stripe-go/v81/webhookendpoint"
|
|
|
|
"github.com/perfect-panel/server/pkg/logger"
|
|
"github.com/stripe/stripe-go/v81"
|
|
"github.com/stripe/stripe-go/v81/customer"
|
|
"github.com/stripe/stripe-go/v81/ephemeralkey"
|
|
"github.com/stripe/stripe-go/v81/paymentintent"
|
|
"github.com/stripe/stripe-go/v81/paymentmethod"
|
|
"github.com/stripe/stripe-go/v81/webhook"
|
|
)
|
|
|
|
const APIVersion = "2024-04-10"
|
|
|
|
type Config struct {
|
|
PublicKey string
|
|
SecretKey string
|
|
WebhookSecret string
|
|
}
|
|
|
|
type User struct {
|
|
UserId int64
|
|
Email string
|
|
}
|
|
type NotifyResult struct {
|
|
EventType string
|
|
OrderNo string
|
|
TradeNo string
|
|
Method string
|
|
UserId int64
|
|
Amount int64
|
|
}
|
|
type Order struct {
|
|
OrderNo string
|
|
Subscribe string
|
|
Amount int64
|
|
Currency string
|
|
Payment string
|
|
}
|
|
|
|
type Client struct {
|
|
Config
|
|
}
|
|
|
|
type PaymentSheet struct {
|
|
ClientSecret string
|
|
EphemeralKey string
|
|
Customer string
|
|
PublishableKey string
|
|
TradeNo string
|
|
}
|
|
|
|
func NewClient(config Config) *Client {
|
|
return &Client{
|
|
Config: config,
|
|
}
|
|
}
|
|
|
|
func (c *Client) CreatePaymentSheet(order *Order, user *User) (*PaymentSheet, error) {
|
|
stripe.Key = c.SecretKey
|
|
// Create a new Stripe customer if it does not exist
|
|
customerDataRes, err := c.SearchStripeCustomer(user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if customerDataRes == nil {
|
|
customerDataRes, err = c.CreateCustomer(user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
// Create Ephemeral Key
|
|
ekParams := &stripe.EphemeralKeyParams{
|
|
Customer: stripe.String(customerDataRes.ID),
|
|
StripeVersion: stripe.String(APIVersion),
|
|
}
|
|
ek, err := ephemeralkey.New(ekParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Create Payment Intent
|
|
params := &stripe.PaymentIntentParams{
|
|
Amount: stripe.Int64(order.Amount),
|
|
Customer: stripe.String(customerDataRes.ID),
|
|
Currency: stripe.String(order.Currency),
|
|
PaymentMethodTypes: []*string{
|
|
stripe.String(order.Payment),
|
|
},
|
|
Metadata: map[string]string{
|
|
"order_no": order.OrderNo,
|
|
"user_id": strconv.FormatInt(user.UserId, 10),
|
|
"subscribe": order.Subscribe,
|
|
},
|
|
}
|
|
result, err := paymentintent.New(params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PaymentSheet{
|
|
ClientSecret: result.ClientSecret,
|
|
EphemeralKey: ek.Secret,
|
|
Customer: customerDataRes.ID,
|
|
PublishableKey: c.PublicKey,
|
|
TradeNo: result.ID,
|
|
}, nil
|
|
}
|
|
|
|
// SearchStripeCustomer Search for a Stripe customer by email or user ID
|
|
func (c *Client) SearchStripeCustomer(user *User) (*stripe.Customer, error) {
|
|
stripe.Key = c.SecretKey
|
|
params := &stripe.CustomerSearchParams{}
|
|
if user.Email != "" {
|
|
params.SearchParams.Query = fmt.Sprintf("email:'%s'", user.Email)
|
|
} else {
|
|
params.SearchParams.Query = fmt.Sprintf("metadata['user_id']:'%d'", user.UserId)
|
|
}
|
|
result := customer.Search(params)
|
|
if result.Err() != nil {
|
|
fmt.Printf("Error: %v\n", result.Err().Error())
|
|
return nil, result.Err()
|
|
}
|
|
|
|
if len(result.CustomerSearchResult().Data) != 0 {
|
|
return result.CustomerSearchResult().Data[0], nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// CreateCustomer Create a new Stripe customer
|
|
func (c *Client) CreateCustomer(user *User) (*stripe.Customer, error) {
|
|
stripe.Key = c.SecretKey
|
|
customerData := &stripe.CustomerParams{}
|
|
if user.Email != "" {
|
|
customerData.Email = &user.Email
|
|
}
|
|
customerData.AddMetadata("user_id", strconv.FormatInt(user.UserId, 10))
|
|
return customer.New(customerData)
|
|
}
|
|
|
|
// QueryOrderStatus Query the status of the order
|
|
func (c *Client) QueryOrderStatus(orderNo string) (bool, error) {
|
|
stripe.Key = c.SecretKey
|
|
intent, err := paymentintent.Get(orderNo, nil)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return intent.Status == "succeeded", err
|
|
}
|
|
|
|
// ParseNotify
|
|
func (c *Client) ParseNotify(payload []byte, signature string) (*NotifyResult, error) {
|
|
event, err := webhook.ConstructEvent(payload, signature, c.Config.WebhookSecret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var paymentIntent stripe.PaymentIntent
|
|
err = json.Unmarshal(event.Data.Raw, &paymentIntent)
|
|
if err != nil {
|
|
logger.Error("Failed to unmarshal payment intent", logger.Field("error", err.Error()))
|
|
return nil, err
|
|
}
|
|
orderNo := paymentIntent.Metadata["order_no"]
|
|
userId := paymentIntent.Metadata["user_id"]
|
|
var method string
|
|
if paymentIntent.PaymentMethod != nil && paymentIntent.PaymentMethod.ID != "" {
|
|
fmt.Println("paymentMethod:", paymentIntent.PaymentMethod.ID)
|
|
result, err := c.RetrievePaymentMethod(paymentIntent.PaymentMethod.ID)
|
|
if err != nil {
|
|
logger.Error("[stripe] Payment callback query payment method error", logger.Field("errors", err.Error()))
|
|
}
|
|
if result != nil {
|
|
method = string(result.Type)
|
|
}
|
|
}
|
|
// userId string 转 int64
|
|
uid, _ := strconv.ParseInt(userId, 10, 64)
|
|
return &NotifyResult{
|
|
EventType: string(event.Type),
|
|
OrderNo: orderNo,
|
|
TradeNo: paymentIntent.ID,
|
|
UserId: uid,
|
|
Method: method,
|
|
Amount: paymentIntent.Amount,
|
|
}, nil
|
|
}
|
|
|
|
// RetrievePaymentMethod 查询支付方式
|
|
func (c *Client) RetrievePaymentMethod(id string) (*stripe.PaymentMethod, error) {
|
|
stripe.Key = c.SecretKey
|
|
return paymentmethod.Get(id, nil)
|
|
}
|
|
|
|
// CreateWebhookEndpoint 创建 webhook endpoint
|
|
func (c *Client) CreateWebhookEndpoint(url string) (*stripe.WebhookEndpoint, error) {
|
|
stripe.Key = c.SecretKey
|
|
params := &stripe.WebhookEndpointParams{
|
|
URL: stripe.String(url),
|
|
EnabledEvents: []*string{
|
|
stripe.String("payment_intent.succeeded"),
|
|
stripe.String("payment_intent.payment_failed"),
|
|
},
|
|
}
|
|
return webhookendpoint.New(params)
|
|
}
|