264 lines
8.4 KiB
Go
264 lines
8.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"git.k6n.net/go-cart-actor/pkg/cart"
|
|
messages "git.k6n.net/go-cart-actor/proto/checkout"
|
|
adyenCheckout "github.com/adyen/adyen-go-api-library/v21/src/checkout"
|
|
"github.com/adyen/adyen-go-api-library/v21/src/common"
|
|
"github.com/adyen/adyen-go-api-library/v21/src/hmacvalidator"
|
|
"github.com/adyen/adyen-go-api-library/v21/src/webhook"
|
|
"github.com/google/uuid"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
type SessionRequest struct {
|
|
SessionId *string `json:"sessionId,omitempty"`
|
|
SessionResult string `json:"sessionResult"`
|
|
SessionData *string `json:"sessionData,omitempty"`
|
|
}
|
|
|
|
// func (s *CheckoutPoolServer) AdyenSessionHandler(w http.ResponseWriter, r *http.Request, cartId cart.CartId) error {
|
|
|
|
// grain, err := s.Get(r.Context(), uint64(cartId))
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// if r.Method == http.MethodGet {
|
|
// service := s.adyenClient.Checkout()
|
|
// req := service.PaymentsApi.GetResultOfPaymentSessionInput(pa).SessionResult(payload.SessionResult)
|
|
// res, _, err := service.PaymentsApi.GetResultOfPaymentSession(r.Context(), req)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// return s.WriteResult(w, res)
|
|
// } else {
|
|
// payload := &SessionRequest{}
|
|
// if err := json.NewDecoder(r.Body).Decode(payload); err != nil {
|
|
// return err
|
|
// }
|
|
// service := s.adyenClient.Checkout()
|
|
// req := service.PaymentsApi.GetResultOfPaymentSessionInput(payload.SessionId).SessionResult(payload.SessionResult)
|
|
// res, _, err := service.PaymentsApi.GetResultOfPaymentSession(r.Context(), req)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// return s.WriteResult(w, res)
|
|
// }
|
|
|
|
// }
|
|
|
|
func getCheckoutIdFromNotificationItem(item webhook.NotificationRequestItem) (uint64, error) {
|
|
cartId, ok := cart.ParseCartId(item.MerchantReference)
|
|
if !ok {
|
|
log.Printf("The notification does not have a valid cartId: %s", item.MerchantReference)
|
|
return 0, errors.New("invalid cart id")
|
|
}
|
|
return uint64(cartId), nil
|
|
}
|
|
|
|
func (s *CheckoutPoolServer) AdyenHookHandler(w http.ResponseWriter, r *http.Request) {
|
|
var notificationRequest webhook.Webhook
|
|
service := s.adyenClient.Checkout()
|
|
if err := json.NewDecoder(r.Body).Decode(¬ificationRequest); err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
for _, notificationItem := range *notificationRequest.NotificationItems {
|
|
item := notificationItem.NotificationRequestItem
|
|
log.Printf("Recieved notification event code: %s, %+v", item.EventCode, item)
|
|
|
|
isValid := hmacvalidator.ValidateHmac(item, hmacKey)
|
|
if !isValid {
|
|
log.Printf("notification hmac not valid %s, %v", item.EventCode, item)
|
|
http.Error(w, "Invalid HMAC", http.StatusUnauthorized)
|
|
return
|
|
} else {
|
|
// Marshal item data for PaymentEvent
|
|
dataBytes, err := json.Marshal(item)
|
|
if err != nil {
|
|
log.Printf("error marshaling item: %v", err)
|
|
http.Error(w, "Error marshaling item", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
switch item.EventCode {
|
|
case "CAPTURE":
|
|
checkoutId, err := getCheckoutIdFromNotificationItem(item)
|
|
if err != nil {
|
|
log.Printf("Could not get checkout id: %v", err)
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
log.Printf("Capture status: %v", item.Success)
|
|
isSuccess := item.Success == "true"
|
|
|
|
// If successful, apply payment completed
|
|
//if isSuccess {
|
|
if err := s.applyAnywhere(r.Context(), checkoutId,
|
|
&messages.PaymentEvent{
|
|
PaymentId: item.PspReference,
|
|
Success: isSuccess,
|
|
Name: item.EventCode,
|
|
Data: &anypb.Any{Value: dataBytes},
|
|
}, &messages.PaymentCompleted{
|
|
PaymentId: item.PspReference,
|
|
Status: item.Success,
|
|
Amount: item.Amount.Value,
|
|
Currency: item.Amount.Currency,
|
|
ProcessorReference: &item.PspReference,
|
|
CompletedAt: timestamppb.New(time.Now()),
|
|
}); err != nil {
|
|
http.Error(w, "Message not parsed", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
//}
|
|
|
|
case "AUTHORISATION":
|
|
|
|
isSuccess := item.Success == "true"
|
|
log.Printf("Handling auth: %+v", item)
|
|
checkoutId, err := getCheckoutIdFromNotificationItem(item)
|
|
if err != nil {
|
|
log.Printf("Could not get checkout id: %v", err)
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
msgs := []proto.Message{
|
|
&messages.PaymentEvent{
|
|
PaymentId: item.PspReference,
|
|
Success: isSuccess,
|
|
Name: item.EventCode,
|
|
Data: &anypb.Any{Value: dataBytes},
|
|
},
|
|
}
|
|
if isSuccess {
|
|
msgs = append(msgs, &messages.PaymentCompleted{
|
|
PaymentId: item.PspReference,
|
|
Status: item.Success,
|
|
Amount: item.Amount.Value,
|
|
})
|
|
} else {
|
|
msgs = append(msgs, &messages.PaymentDeclined{
|
|
PaymentId: item.PspReference,
|
|
Message: item.Reason,
|
|
})
|
|
}
|
|
if err := s.applyAnywhere(r.Context(), checkoutId, msgs...); err != nil {
|
|
log.Printf("error applying authorization event: %v", err)
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// If successful authorization, trigger capture
|
|
if isSuccess {
|
|
|
|
pspReference := item.PspReference
|
|
uid := uuid.New().String()
|
|
ref := cart.CartId(checkoutId).String()
|
|
req := service.ModificationsApi.CaptureAuthorisedPaymentInput(pspReference).IdempotencyKey(uid).PaymentCaptureRequest(adyenCheckout.PaymentCaptureRequest{
|
|
Amount: adyenCheckout.Amount(item.Amount),
|
|
MerchantAccount: "ElgigantenECOM",
|
|
Reference: &ref,
|
|
})
|
|
res, _, err := service.ModificationsApi.CaptureAuthorisedPayment(r.Context(), req)
|
|
if err != nil {
|
|
log.Printf("Error capturing payment: %v", err)
|
|
} else {
|
|
log.Printf("Payment captured successfully: %+v", res)
|
|
s.Apply(r.Context(), checkoutId, &messages.OrderCreated{
|
|
OrderId: res.PaymentPspReference,
|
|
Status: item.EventCode,
|
|
})
|
|
}
|
|
}
|
|
default:
|
|
log.Printf("Unknown event code: %s", item.EventCode)
|
|
log.Printf("Item data: %+v", item)
|
|
isSuccess := item.Success == "true"
|
|
checkoutId, err := getCheckoutIdFromNotificationItem(item)
|
|
if err != nil {
|
|
log.Printf("Could not get checkout id: %v", err)
|
|
|
|
} else {
|
|
if err := s.applyAnywhere(r.Context(), checkoutId, &messages.PaymentEvent{
|
|
PaymentId: item.PspReference,
|
|
Success: isSuccess,
|
|
Name: item.EventCode,
|
|
Data: &anypb.Any{Value: dataBytes},
|
|
}); err != nil {
|
|
log.Printf("error applying payment event: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
w.WriteHeader(http.StatusAccepted)
|
|
|
|
}
|
|
|
|
func (s *CheckoutPoolServer) AdyenReturnHandler(w http.ResponseWriter, r *http.Request) {
|
|
log.Println("Redirect received")
|
|
|
|
service := s.adyenClient.Checkout()
|
|
|
|
req := service.PaymentsApi.GetResultOfPaymentSessionInput(r.URL.Query().Get("sessionId"))
|
|
|
|
res, httpRes, err := service.PaymentsApi.GetResultOfPaymentSession(r.Context(), req)
|
|
log.Printf("got payment session %+v", res)
|
|
|
|
dreq := service.PaymentsApi.PaymentsDetailsInput()
|
|
dreq = dreq.PaymentDetailsRequest(adyenCheckout.PaymentDetailsRequest{
|
|
Details: adyenCheckout.PaymentCompletionDetails{
|
|
RedirectResult: common.PtrString(r.URL.Query().Get("redirectResult")),
|
|
Payload: common.PtrString(r.URL.Query().Get("payload")),
|
|
},
|
|
})
|
|
|
|
dres, httpRes, err := service.PaymentsApi.PaymentsDetails(r.Context(), dreq)
|
|
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
log.Printf("Payment details response: %+v", dres)
|
|
|
|
if !common.IsNil(dres.PspReference) && *dres.PspReference != "" {
|
|
var redirectURL string
|
|
// Conditionally handle different result codes for the shopper
|
|
switch *dres.ResultCode {
|
|
case "Authorised":
|
|
redirectURL = "/result/success"
|
|
case "Pending", "Received":
|
|
redirectURL = "/result/pending"
|
|
case "Refused":
|
|
redirectURL = "/result/failed"
|
|
default:
|
|
reason := ""
|
|
if dres.RefusalReason != nil {
|
|
reason = *dres.RefusalReason
|
|
} else {
|
|
reason = *dres.ResultCode
|
|
}
|
|
log.Printf("Payment failed: %s", reason)
|
|
redirectURL = fmt.Sprintf("/result/error?reason=%s", url.QueryEscape(reason))
|
|
}
|
|
http.Redirect(w, r, redirectURL, http.StatusFound)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(httpRes.StatusCode)
|
|
json.NewEncoder(w).Encode(httpRes.Status)
|
|
}
|