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) }