add adyen
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.k6n.net/go-cart-actor/pkg/cart"
|
"git.k6n.net/go-cart-actor/pkg/cart"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/checkout"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckoutMeta carries the external / URL metadata required to build a
|
// CheckoutMeta carries the external / URL metadata required to build a
|
||||||
@@ -118,3 +120,61 @@ func BuildCheckoutOrderPayload(grain *cart.CartGrain, meta *CheckoutMeta) ([]byt
|
|||||||
|
|
||||||
return payload, order, nil
|
return payload, order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildAdyenCheckoutSession(grain *cart.CartGrain, meta *CheckoutMeta) (*checkout.CreateCheckoutSessionRequest, error) {
|
||||||
|
if grain == nil {
|
||||||
|
return nil, fmt.Errorf("nil grain")
|
||||||
|
}
|
||||||
|
if meta == nil {
|
||||||
|
return nil, fmt.Errorf("nil checkout meta")
|
||||||
|
}
|
||||||
|
|
||||||
|
currency := meta.Currency
|
||||||
|
if currency == "" {
|
||||||
|
currency = "SEK"
|
||||||
|
}
|
||||||
|
country := meta.Country
|
||||||
|
if country == "" {
|
||||||
|
country = "SE"
|
||||||
|
}
|
||||||
|
|
||||||
|
lineItems := make([]checkout.LineItem, 0, len(grain.Items)+len(grain.Deliveries))
|
||||||
|
|
||||||
|
// Item lines
|
||||||
|
for _, it := range grain.Items {
|
||||||
|
if it == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lineItems = append(lineItems, checkout.LineItem{
|
||||||
|
Quantity: common.PtrInt64(int64(it.Quantity)),
|
||||||
|
AmountIncludingTax: common.PtrInt64(it.TotalPrice.IncVat),
|
||||||
|
Description: common.PtrString(it.Meta.Name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delivery lines
|
||||||
|
for _, d := range grain.Deliveries {
|
||||||
|
if d == nil || d.Price.IncVat <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lineItems = append(lineItems, checkout.LineItem{
|
||||||
|
Quantity: common.PtrInt64(1),
|
||||||
|
AmountIncludingTax: common.PtrInt64(d.Price.IncVat),
|
||||||
|
Description: common.PtrString("Delivery"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &checkout.CreateCheckoutSessionRequest{
|
||||||
|
Reference: grain.Id.String(),
|
||||||
|
Amount: checkout.Amount{
|
||||||
|
Value: grain.TotalPrice.IncVat,
|
||||||
|
Currency: currency,
|
||||||
|
},
|
||||||
|
CountryCode: common.PtrString(country),
|
||||||
|
MerchantAccount: "ElgigantenECOM",
|
||||||
|
Channel: common.PtrString("Web"),
|
||||||
|
ReturnUrl: meta.Checkout,
|
||||||
|
LineItems: lineItems,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"git.k6n.net/go-cart-actor/pkg/actor"
|
"git.k6n.net/go-cart-actor/pkg/actor"
|
||||||
"git.k6n.net/go-cart-actor/pkg/cart"
|
"git.k6n.net/go-cart-actor/pkg/cart"
|
||||||
"git.k6n.net/go-cart-actor/pkg/messages"
|
"git.k6n.net/go-cart-actor/pkg/messages"
|
||||||
|
|
||||||
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
||||||
amqp "github.com/rabbitmq/amqp091-go"
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import (
|
|||||||
"git.k6n.net/go-cart-actor/pkg/promotions"
|
"git.k6n.net/go-cart-actor/pkg/promotions"
|
||||||
"git.k6n.net/go-cart-actor/pkg/proxy"
|
"git.k6n.net/go-cart-actor/pkg/proxy"
|
||||||
"git.k6n.net/go-cart-actor/pkg/voucher"
|
"git.k6n.net/go-cart-actor/pkg/voucher"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/adyen"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/common"
|
||||||
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
@@ -154,9 +156,14 @@ func main() {
|
|||||||
log.Fatalf("Error creating cart pool: %v\n", err)
|
log.Fatalf("Error creating cart pool: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adyenClient := adyen.NewClient(&common.Config{
|
||||||
|
ApiKey: os.Getenv("ADYEN_API_KEY"),
|
||||||
|
Environment: common.TestEnv,
|
||||||
|
})
|
||||||
|
|
||||||
klarnaClient := NewKlarnaClient(KlarnaPlaygroundUrl, os.Getenv("KLARNA_API_USERNAME"), os.Getenv("KLARNA_API_PASSWORD"))
|
klarnaClient := NewKlarnaClient(KlarnaPlaygroundUrl, os.Getenv("KLARNA_API_USERNAME"), os.Getenv("KLARNA_API_PASSWORD"))
|
||||||
|
|
||||||
syncedServer := NewPoolServer(pool, fmt.Sprintf("%s, %s", name, podIp), klarnaClient, inventoryService, inventoryReservationService)
|
syncedServer := NewPoolServer(pool, fmt.Sprintf("%s, %s", name, podIp), klarnaClient, inventoryService, inventoryReservationService, adyenClient)
|
||||||
|
|
||||||
app := &App{
|
app := &App{
|
||||||
pool: pool,
|
pool: pool,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -15,6 +16,9 @@ import (
|
|||||||
"git.k6n.net/go-cart-actor/pkg/cart"
|
"git.k6n.net/go-cart-actor/pkg/cart"
|
||||||
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
messages "git.k6n.net/go-cart-actor/pkg/messages"
|
||||||
"git.k6n.net/go-cart-actor/pkg/voucher"
|
"git.k6n.net/go-cart-actor/pkg/voucher"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/adyen"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/hmacvalidator"
|
||||||
|
"github.com/adyen/adyen-go-api-library/v14/src/webhook"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
"github.com/matst80/go-redis-inventory/pkg/inventory"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@@ -44,17 +48,19 @@ type PoolServer struct {
|
|||||||
actor.GrainPool[*cart.CartGrain]
|
actor.GrainPool[*cart.CartGrain]
|
||||||
pod_name string
|
pod_name string
|
||||||
klarnaClient *KlarnaClient
|
klarnaClient *KlarnaClient
|
||||||
|
adyenClient *adyen.APIClient
|
||||||
inventoryService inventory.InventoryService
|
inventoryService inventory.InventoryService
|
||||||
reservationService inventory.CartReservationService
|
reservationService inventory.CartReservationService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPoolServer(pool actor.GrainPool[*cart.CartGrain], pod_name string, klarnaClient *KlarnaClient, inventoryService inventory.InventoryService, inventoryReservationService inventory.CartReservationService) *PoolServer {
|
func NewPoolServer(pool actor.GrainPool[*cart.CartGrain], pod_name string, klarnaClient *KlarnaClient, inventoryService inventory.InventoryService, inventoryReservationService inventory.CartReservationService, adyenClient *adyen.APIClient) *PoolServer {
|
||||||
srv := &PoolServer{
|
srv := &PoolServer{
|
||||||
GrainPool: pool,
|
GrainPool: pool,
|
||||||
pod_name: pod_name,
|
pod_name: pod_name,
|
||||||
klarnaClient: klarnaClient,
|
klarnaClient: klarnaClient,
|
||||||
inventoryService: inventoryService,
|
inventoryService: inventoryService,
|
||||||
reservationService: inventoryReservationService,
|
reservationService: inventoryReservationService,
|
||||||
|
adyenClient: adyenClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
return srv
|
return srv
|
||||||
@@ -537,8 +543,8 @@ func (s *PoolServer) ProxyHandler(fn func(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tracer = otel.Tracer(name)
|
tracer = otel.Tracer(name)
|
||||||
|
hmacKey = os.Getenv("ADYEN_HMAC")
|
||||||
meter = otel.Meter(name)
|
meter = otel.Meter(name)
|
||||||
logger = otelslog.NewLogger(name)
|
logger = otelslog.NewLogger(name)
|
||||||
proxyCalls metric.Int64Counter
|
proxyCalls metric.Int64Counter
|
||||||
@@ -716,6 +722,35 @@ func (s *PoolServer) CreateCheckoutOrderHandler(w http.ResponseWriter, r *http.R
|
|||||||
return s.WriteResult(w, reply)
|
return s.WriteResult(w, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PoolServer) AdyenSessionHandler(w http.ResponseWriter, r *http.Request, cartId cart.CartId) error {
|
||||||
|
|
||||||
|
grain, err := s.Get(r.Context(), uint64(cartId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
host := getOriginalHost(r)
|
||||||
|
country := getCountryFromHost(host)
|
||||||
|
meta := &CheckoutMeta{
|
||||||
|
Terms: fmt.Sprintf("https://%s/terms", host),
|
||||||
|
Checkout: fmt.Sprintf("https://%s/checkout?order_id={checkout.order.id}", host),
|
||||||
|
Confirmation: fmt.Sprintf("https://%s/confirmation/{checkout.order.id}", host),
|
||||||
|
Country: country,
|
||||||
|
Currency: getCurrency(country),
|
||||||
|
Locale: getLocale(country),
|
||||||
|
}
|
||||||
|
sessionData, err := BuildAdyenCheckoutSession(grain, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service := s.adyenClient.Checkout()
|
||||||
|
req := service.PaymentsApi.SessionsInput().CreateCheckoutSessionRequest(*sessionData)
|
||||||
|
res, _, err := service.PaymentsApi.Sessions(r.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.WriteResult(w, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PoolServer) Serve(mux *http.ServeMux) {
|
func (s *PoolServer) Serve(mux *http.ServeMux) {
|
||||||
|
|
||||||
// mux.HandleFunc("OPTIONS /cart", func(w http.ResponseWriter, r *http.Request) {
|
// mux.HandleFunc("OPTIONS /cart", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -739,6 +774,24 @@ func (s *PoolServer) Serve(mux *http.ServeMux) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFunc("/adyen_hook", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var notificationRequest webhook.Webhook
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(¬ificationRequest); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, notification := range notificationRequest.GetNotificationItems() {
|
||||||
|
isValid := hmacvalidator.ValidateHmac(*notification, hmacKey)
|
||||||
|
if !isValid {
|
||||||
|
http.Error(w, "Invalid HMAC", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Printf("Recieved notification event code: %s, %v", notification.EventCode, notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
})
|
||||||
|
|
||||||
handleFunc("GET /cart", CookieCartIdHandler(s.ProxyHandler(s.GetCartHandler)))
|
handleFunc("GET /cart", CookieCartIdHandler(s.ProxyHandler(s.GetCartHandler)))
|
||||||
handleFunc("GET /cart/add/{sku}", CookieCartIdHandler(s.ProxyHandler(s.AddSkuToCartHandler)))
|
handleFunc("GET /cart/add/{sku}", CookieCartIdHandler(s.ProxyHandler(s.AddSkuToCartHandler)))
|
||||||
handleFunc("POST /cart/add", CookieCartIdHandler(s.ProxyHandler(s.AddMultipleItemHandler)))
|
handleFunc("POST /cart/add", CookieCartIdHandler(s.ProxyHandler(s.AddMultipleItemHandler)))
|
||||||
@@ -754,6 +807,7 @@ func (s *PoolServer) Serve(mux *http.ServeMux) {
|
|||||||
handleFunc("PUT /cart/subscription-details", CookieCartIdHandler(s.ProxyHandler(s.SubscriptionDetailsHandler)))
|
handleFunc("PUT /cart/subscription-details", CookieCartIdHandler(s.ProxyHandler(s.SubscriptionDetailsHandler)))
|
||||||
handleFunc("DELETE /cart/voucher/{voucherId}", CookieCartIdHandler(s.ProxyHandler(s.RemoveVoucherHandler)))
|
handleFunc("DELETE /cart/voucher/{voucherId}", CookieCartIdHandler(s.ProxyHandler(s.RemoveVoucherHandler)))
|
||||||
handleFunc("PUT /cart/user", CookieCartIdHandler(s.ProxyHandler(s.SetUserIdHandler)))
|
handleFunc("PUT /cart/user", CookieCartIdHandler(s.ProxyHandler(s.SetUserIdHandler)))
|
||||||
|
handleFunc("GET /cart/adyen-session", CookieCartIdHandler(s.ProxyHandler(s.AdyenSessionHandler)))
|
||||||
handleFunc("PUT /cart/item/{itemId}/marking", CookieCartIdHandler(s.ProxyHandler(s.LineItemMarkingHandler)))
|
handleFunc("PUT /cart/item/{itemId}/marking", CookieCartIdHandler(s.ProxyHandler(s.LineItemMarkingHandler)))
|
||||||
handleFunc("DELETE /cart/item/{itemId}/marking", CookieCartIdHandler(s.ProxyHandler(s.RemoveLineItemMarkingHandler)))
|
handleFunc("DELETE /cart/item/{itemId}/marking", CookieCartIdHandler(s.ProxyHandler(s.RemoveLineItemMarkingHandler)))
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,16 @@ spec:
|
|||||||
value: "10.10.3.18:6379"
|
value: "10.10.3.18:6379"
|
||||||
- name: REDIS_PASSWORD
|
- name: REDIS_PASSWORD
|
||||||
value: "slaskredis"
|
value: "slaskredis"
|
||||||
|
- name: ADYEN_HMAC
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: adyen
|
||||||
|
key: HMAC
|
||||||
|
- name: ADYEN_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: adyen
|
||||||
|
key: API_KEY
|
||||||
- name: KLARNA_API_USERNAME
|
- name: KLARNA_API_USERNAME
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
@@ -185,6 +195,16 @@ spec:
|
|||||||
value: "service.name=cart,service.version=0.1.2"
|
value: "service.name=cart,service.version=0.1.2"
|
||||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||||
value: "http://otel-debug-service.monitoring:4317"
|
value: "http://otel-debug-service.monitoring:4317"
|
||||||
|
- name: ADYEN_HMAC
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: adyen
|
||||||
|
key: HMAC
|
||||||
|
- name: ADYEN_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: adyen
|
||||||
|
key: API_KEY
|
||||||
- name: KLARNA_API_PASSWORD
|
- name: KLARNA_API_PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
@@ -290,6 +310,16 @@ spec:
|
|||||||
value: "service.name=cart,service.version=0.1.2"
|
value: "service.name=cart,service.version=0.1.2"
|
||||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||||
value: "http://otel-debug-service.monitoring:4317"
|
value: "http://otel-debug-service.monitoring:4317"
|
||||||
|
- name: ADYEN_HMAC
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: adyen
|
||||||
|
key: HMAC
|
||||||
|
- name: ADYEN_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: adyen
|
||||||
|
key: API_KEY
|
||||||
- name: KLARNA_API_USERNAME
|
- name: KLARNA_API_USERNAME
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
@@ -472,4 +502,4 @@ spec:
|
|||||||
app: cart-inventory
|
app: cart-inventory
|
||||||
ports:
|
ports:
|
||||||
- name: web
|
- name: web
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -31,6 +31,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.14.4 // indirect
|
github.com/RoaringBitmap/roaring/v2 v2.14.4 // indirect
|
||||||
|
github.com/adyen/adyen-go-api-library/v14 v14.0.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||||
|
|||||||
Reference in New Issue
Block a user