package stripe import ( "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/stripe/stripe-go/v72" "github.com/stripe/stripe-go/v72/checkout/session" "github.com/stripe/stripe-go/v72/paymentintent" "log/slog" database2 "payment-poc/domain/database" "payment-poc/domain/state" ) type Service struct { ApiKey string BackendUrl string } func (s *Service) UpdatePayment(entry database2.PaymentEntry) (updatedEntry database2.PaymentEntry, err error) { client := paymentintent.Client{ B: stripe.GetBackend(stripe.APIBackend), Key: s.ApiKey, } pi, err := client.Get(*entry.PaymentIntentId, nil) if err != nil { return entry, err } newState := determineState(pi.Status) if entry.State != newState && newState != "" { slog.Info("updated state", "entry_id", entry.Id.String(), "state", entry.State, "new_state", newState) if pi.AmountReceived > 0 { entry.Amount = &pi.AmountReceived } entry.State = newState } return entry, nil } func determineState(status stripe.PaymentIntentStatus) state.PaymentState { switch status { case stripe.PaymentIntentStatusCanceled: return state.StateCanceled case stripe.PaymentIntentStatusProcessing: return state.StatePending case stripe.PaymentIntentStatusRequiresAction: return state.StatePending case stripe.PaymentIntentStatusRequiresCapture: return state.StateAccepted case stripe.PaymentIntentStatusRequiresConfirmation: return state.StatePending case stripe.PaymentIntentStatusRequiresPaymentMethod: return state.StateVoided case stripe.PaymentIntentStatusSucceeded: return state.StateCompleted } return "" } func (s *Service) CreatePaymentUrl(entry database2.PaymentEntry) (database2.PaymentEntry, string, error) { entry, url, err := s.InitializePayment(entry) if err != nil { return entry, "", err } return entry, url, nil } func (s *Service) InitializePayment(entry database2.PaymentEntry) (database2.PaymentEntry, string, error) { currency := string(stripe.CurrencyEUR) productName := "Example product" productDescription := "Simple example product" params := &stripe.CheckoutSessionParams{ LineItems: []*stripe.CheckoutSessionLineItemParams{ { PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: ¤cy, ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: &productName, Description: &productDescription, }, UnitAmount: &entry.TotalAmount, }, Quantity: stripe.Int64(1), }, }, Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), PaymentIntentData: &stripe.CheckoutSessionPaymentIntentDataParams{ CaptureMethod: stripe.String("manual"), }, SuccessURL: stripe.String(s.BackendUrl + "/stripe/success?token=" + entry.Id.String()), CancelURL: stripe.String(s.BackendUrl + "/stripe/cancel?token=" + entry.Id.String()), } client := session.Client{ B: stripe.GetBackend(stripe.APIBackend), Key: s.ApiKey, } result, err := client.New(params) if err != nil { return database2.PaymentEntry{}, "", err } entry.State = state.StateInitialized entry.PaymentIntentId = &result.PaymentIntent.ID return entry, result.URL, nil } func (s *Service) CompleteTransaction(entry database2.PaymentEntry, amount int64) (database2.PaymentEntry, error) { params := &stripe.PaymentIntentCaptureParams{ AmountToCapture: stripe.Int64(amount), } client := paymentintent.Client{ B: stripe.GetBackend(stripe.APIBackend), Key: s.ApiKey, } pi, err := client.Capture(*entry.PaymentIntentId, params) if err != nil { return database2.PaymentEntry{}, err } slog.Info("received state on completion", "entry_id", entry.Id.String(), "state", entry.State, "new_state", pi.Status) newState := determineState(pi.Status) entry.State = newState if newState == state.StateCompleted || newState == state.StatePending { entry.Amount = &pi.AmountReceived } return entry, nil } func (s *Service) CancelTransaction(entry database2.PaymentEntry) (database2.PaymentEntry, error) { params := &stripe.PaymentIntentCancelParams{} client := paymentintent.Client{ B: stripe.GetBackend(stripe.APIBackend), Key: s.ApiKey, } pi, err := client.Cancel(*entry.PaymentIntentId, params) if err != nil { return database2.PaymentEntry{}, err } slog.Info("received state on completion", "entry_id", entry.Id.String(), "state", entry.State, "new_state", pi.Status) if pi.Status == stripe.PaymentIntentStatusCanceled { entry.State = state.StateCanceled } return entry, nil } func (s *Service) HandleResponse(c *gin.Context, provider *database2.PaymentEntryProvider, paymentState state.PaymentState) (string, error) { id := uuid.MustParse(c.Query("token")) entry, err := provider.FetchById(id) if err != nil { return "", err } entry.State = paymentState if _, err := provider.UpdateEntry(entry); err != nil { return "", err } slog.Info("received authorization response", "entry_id", entry.Id.String(), "state", entry.State) return "/entries/" + entry.Id.String(), nil }