refactor klarna stuff
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
messages "git.tornberg.me/go-cart-actor/proto"
|
messages "git.tornberg.me/go-cart-actor/proto"
|
||||||
klarna "github.com/Flaconi/go-klarna"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CartId [16]byte
|
type CartId [16]byte
|
||||||
@@ -430,7 +429,7 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
|
|||||||
err = fmt.Errorf("expected CreateCheckoutOrder")
|
err = fmt.Errorf("expected CreateCheckoutOrder")
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
orderLines := make([]*klarna.Line, 0, len(c.Items))
|
orderLines := make([]*Line, 0, len(c.Items))
|
||||||
totalTax := 0
|
totalTax := 0
|
||||||
c.PaymentInProgress = true
|
c.PaymentInProgress = true
|
||||||
c.Processing = true
|
c.Processing = true
|
||||||
@@ -438,7 +437,7 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
|
|||||||
total := int(item.Price) * item.Quantity
|
total := int(item.Price) * item.Quantity
|
||||||
taxAmount := GetTaxAmount(total, item.Tax)
|
taxAmount := GetTaxAmount(total, item.Tax)
|
||||||
totalTax += taxAmount
|
totalTax += taxAmount
|
||||||
orderLines = append(orderLines, &klarna.Line{
|
orderLines = append(orderLines, &Line{
|
||||||
Type: "physical",
|
Type: "physical",
|
||||||
Reference: item.Sku,
|
Reference: item.Sku,
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
@@ -448,10 +447,10 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
|
|||||||
QuantityUnit: "st",
|
QuantityUnit: "st",
|
||||||
TotalAmount: total,
|
TotalAmount: total,
|
||||||
TotalTaxAmount: taxAmount,
|
TotalTaxAmount: taxAmount,
|
||||||
ImageURL: item.Image,
|
ImageURL: fmt.Sprintf("https://www.elgiganten.se%s", item.Image),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
order := klarna.CheckoutOrder{
|
order := CheckoutOrder{
|
||||||
PurchaseCountry: "SE",
|
PurchaseCountry: "SE",
|
||||||
PurchaseCurrency: "SEK",
|
PurchaseCurrency: "SEK",
|
||||||
Locale: "sv-se",
|
Locale: "sv-se",
|
||||||
@@ -459,7 +458,7 @@ func (c *CartGrain) HandleMessage(message *Message, isReplay bool) (*FrameWithPa
|
|||||||
OrderTaxAmount: totalTax,
|
OrderTaxAmount: totalTax,
|
||||||
OrderLines: orderLines,
|
OrderLines: orderLines,
|
||||||
MerchantReference1: c.Id.String(),
|
MerchantReference1: c.Id.String(),
|
||||||
MerchantURLS: &klarna.CheckoutMerchantURLS{
|
MerchantURLS: &CheckoutMerchantURLS{
|
||||||
Terms: msg.Terms,
|
Terms: msg.Terms,
|
||||||
Checkout: msg.Checkout,
|
Checkout: msg.Checkout,
|
||||||
Confirmation: msg.Confirmation,
|
Confirmation: msg.Confirmation,
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -3,12 +3,10 @@ module git.tornberg.me/go-cart-actor
|
|||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Flaconi/go-klarna v0.0.0-20230216165926-e2f708c721d9
|
|
||||||
github.com/matst80/slask-finder v0.0.0-20250418094723-2eb7d6615761
|
github.com/matst80/slask-finder v0.0.0-20250418094723-2eb7d6615761
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
github.com/rabbitmq/amqp091-go v1.10.0
|
||||||
github.com/yudhasubki/netpool v0.0.0-20230717065341-3c1353ca328e
|
github.com/yudhasubki/netpool v0.0.0-20230717065341-3c1353ca328e
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
|
|
||||||
google.golang.org/protobuf v1.36.6
|
google.golang.org/protobuf v1.36.6
|
||||||
k8s.io/api v0.32.3
|
k8s.io/api v0.32.3
|
||||||
k8s.io/apimachinery v0.32.3
|
k8s.io/apimachinery v0.32.3
|
||||||
@@ -51,6 +49,7 @@ require (
|
|||||||
golang.org/x/term v0.31.0 // indirect
|
golang.org/x/term v0.31.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.24.0 // indirect
|
||||||
golang.org/x/time v0.11.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
|
golang.org/x/tools v0.32.0 // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -109,8 +109,6 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
|||||||
104
klarna-client.go
Normal file
104
klarna-client.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KlarnaClient struct {
|
||||||
|
Url string
|
||||||
|
UserName string
|
||||||
|
Password string
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKlarnaClient(url, userName, password string) *KlarnaClient {
|
||||||
|
return &KlarnaClient{
|
||||||
|
Url: url,
|
||||||
|
UserName: userName,
|
||||||
|
Password: password,
|
||||||
|
client: &http.Client{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
KlarnaPlaygroundUrl = "https://api.playground.klarna.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k *KlarnaClient) GetOrder(orderId string) (*CheckoutOrder, error) {
|
||||||
|
req, err := http.NewRequest("GET", k.Url+"/checkout/v3/orders/"+orderId, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.SetBasicAuth(k.UserName, k.Password)
|
||||||
|
|
||||||
|
res, err := k.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
var klarnaOrderResponse CheckoutOrder
|
||||||
|
err = json.NewDecoder(res.Body).Decode(&klarnaOrderResponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &klarnaOrderResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KlarnaClient) CreateOrder(reader io.Reader) (*CheckoutOrder, error) {
|
||||||
|
//bytes.NewReader(reply.Payload)
|
||||||
|
req, err := http.NewRequest("POST", k.Url+"/checkout/v3/orders", reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.SetBasicAuth(k.UserName, k.Password)
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
var klarnaOrderResponse CheckoutOrder
|
||||||
|
err = json.NewDecoder(res.Body).Decode(&klarnaOrderResponse)
|
||||||
|
return &klarnaOrderResponse, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KlarnaClient) UpdateOrder(orderId string, reader io.Reader) (*CheckoutOrder, error) {
|
||||||
|
//bytes.NewReader(reply.Payload)
|
||||||
|
req, err := http.NewRequest("POST", fmt.Sprintf("%s/checkout/v3/orders/%s", k.Url, orderId), reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.SetBasicAuth(k.UserName, k.Password)
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
var klarnaOrderResponse CheckoutOrder
|
||||||
|
err = json.NewDecoder(res.Body).Decode(&klarnaOrderResponse)
|
||||||
|
return &klarnaOrderResponse, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KlarnaClient) AbortOrder(orderId string) error {
|
||||||
|
req, err := http.NewRequest("POST", fmt.Sprintf("%s/checkout/v3/orders/%s/abort", k.Url, orderId), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.SetBasicAuth(k.UserName, k.Password)
|
||||||
|
|
||||||
|
_, err = http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type KlarnaOrderResponse struct {
|
|
||||||
OrderID string `json:"order_id"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
PurchaseCountry string `json:"purchase_country"`
|
|
||||||
PurchaseCurrency string `json:"purchase_currency"`
|
|
||||||
Locale string `json:"locale"`
|
|
||||||
BillingAddress struct {
|
|
||||||
GivenName string `json:"given_name"`
|
|
||||||
FamilyName string `json:"family_name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
StreetAddress string `json:"street_address"`
|
|
||||||
PostalCode string `json:"postal_code"`
|
|
||||||
City string `json:"city"`
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
Country string `json:"country"`
|
|
||||||
} `json:"billing_address"`
|
|
||||||
Customer struct {
|
|
||||||
DateOfBirth string `json:"date_of_birth"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Gender string `json:"gender"`
|
|
||||||
} `json:"customer"`
|
|
||||||
ShippingAddress struct {
|
|
||||||
GivenName string `json:"given_name"`
|
|
||||||
FamilyName string `json:"family_name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
StreetAddress string `json:"street_address"`
|
|
||||||
PostalCode string `json:"postal_code"`
|
|
||||||
City string `json:"city"`
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
Country string `json:"country"`
|
|
||||||
} `json:"shipping_address"`
|
|
||||||
OrderAmount int `json:"order_amount"`
|
|
||||||
OrderTaxAmount int `json:"order_tax_amount"`
|
|
||||||
OrderLines []struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Reference string `json:"reference"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Quantity int `json:"quantity"`
|
|
||||||
QuantityUnit string `json:"quantity_unit"`
|
|
||||||
UnitPrice int `json:"unit_price"`
|
|
||||||
TaxRate int `json:"tax_rate"`
|
|
||||||
TotalAmount int `json:"total_amount"`
|
|
||||||
TotalDiscountAmount int `json:"total_discount_amount"`
|
|
||||||
TotalTaxAmount int `json:"total_tax_amount"`
|
|
||||||
ImageURL string `json:"image_url"`
|
|
||||||
} `json:"order_lines"`
|
|
||||||
MerchantUrls struct {
|
|
||||||
Terms string `json:"terms"`
|
|
||||||
Checkout string `json:"checkout"`
|
|
||||||
Confirmation string `json:"confirmation"`
|
|
||||||
Push string `json:"push"`
|
|
||||||
} `json:"merchant_urls"`
|
|
||||||
MerchantReference1 string `json:"merchant_reference1"`
|
|
||||||
HTMLSnippet string `json:"html_snippet"`
|
|
||||||
StartedAt time.Time `json:"started_at"`
|
|
||||||
CompletedAt time.Time `json:"completed_at"`
|
|
||||||
LastModifiedAt time.Time `json:"last_modified_at"`
|
|
||||||
Options struct {
|
|
||||||
AllowSeparateShippingAddress bool `json:"allow_separate_shipping_address"`
|
|
||||||
DateOfBirthMandatory bool `json:"date_of_birth_mandatory"`
|
|
||||||
RequireValidateCallbackSuccess bool `json:"require_validate_callback_success"`
|
|
||||||
} `json:"options"`
|
|
||||||
ExternalPaymentMethods []interface{} `json:"external_payment_methods"`
|
|
||||||
ExternalCheckouts []interface{} `json:"external_checkouts"`
|
|
||||||
PaymentTypeAllowsIncrease bool `json:"payment_type_allows_increase"`
|
|
||||||
}
|
|
||||||
167
klarna-types.go
Normal file
167
klarna-types.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type (
|
||||||
|
LineType string
|
||||||
|
|
||||||
|
// CheckoutOrder type is the request structure to create a new order from the Checkout API
|
||||||
|
CheckoutOrder struct {
|
||||||
|
ID string `json:"order_id,omitempty"`
|
||||||
|
PurchaseCountry string `json:"purchase_country"`
|
||||||
|
PurchaseCurrency string `json:"purchase_currency"`
|
||||||
|
Locale string `json:"locale"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
BillingAddress *Address `json:"billing_address,omitempty"`
|
||||||
|
ShippingAddress *Address `json:"shipping_address,omitempty"`
|
||||||
|
OrderAmount int `json:"order_amount"`
|
||||||
|
OrderTaxAmount int `json:"order_tax_amount"`
|
||||||
|
OrderLines []*Line `json:"order_lines"`
|
||||||
|
Customer *CheckoutCustomer `json:"customer,omitempty"`
|
||||||
|
MerchantURLS *CheckoutMerchantURLS `json:"merchant_urls"`
|
||||||
|
HTMLSnippet string `json:"html_snippet,omitempty"`
|
||||||
|
MerchantReference1 string `json:"merchant_reference1,omitempty"`
|
||||||
|
MerchantReference2 string `json:"merchant_reference2,omitempty"`
|
||||||
|
StartedAt string `json:"started_at,omitempty"`
|
||||||
|
CompletedAt string `json:"completed_at,omitempty"`
|
||||||
|
LastModifiedAt string `json:"last_modified_at,omitempty"`
|
||||||
|
Options *CheckoutOptions `json:"options,omitempty"`
|
||||||
|
Attachment *Attachment `json:"attachment,omitempty"`
|
||||||
|
ExternalPaymentMethods []*PaymentProvider `json:"external_payment_methods,omitempty"`
|
||||||
|
ExternalCheckouts []*PaymentProvider `json:"external_checkouts,omitempty"`
|
||||||
|
ShippingCountries []string `json:"shipping_countries,omitempty"`
|
||||||
|
ShippingOptions []*ShippingOption `json:"shipping_options,omitempty"`
|
||||||
|
MerchantData string `json:"merchant_data,omitempty"`
|
||||||
|
GUI *GUI `json:"gui,omitempty"`
|
||||||
|
MerchantRequested *AdditionalCheckBox `json:"merchant_requested,omitempty"`
|
||||||
|
SelectedShippingOption *ShippingOption `json:"selected_shipping_option,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUI type wraps the GUI options
|
||||||
|
GUI struct {
|
||||||
|
Options []string `json:"options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShippingOption type is part of the CheckoutOrder structure, represent the shipping options field
|
||||||
|
ShippingOption struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Promo string `json:"promo,omitempty"`
|
||||||
|
Price int `json:"price"`
|
||||||
|
TaxAmount int `json:"tax_amount"`
|
||||||
|
TaxRate int `json:"tax_rate"`
|
||||||
|
Preselected bool `json:"preselected,omitempty"`
|
||||||
|
ShippingMethod string `json:"shipping_method,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaymentProvider type is part of the CheckoutOrder structure, represent the ExternalPaymentMethods and
|
||||||
|
// ExternalCheckouts field
|
||||||
|
PaymentProvider struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
RedirectURL string `json:"redirect_url"`
|
||||||
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
|
Fee int `json:"fee,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Countries []string `json:"countries,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Attachment struct {
|
||||||
|
ContentType string `json:"content_type"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckoutOptions struct {
|
||||||
|
AcquiringChannel string `json:"acquiring_channel,omitempty"`
|
||||||
|
AllowSeparateShippingAddress bool `json:"allow_separate_shipping_address,omitempty"`
|
||||||
|
ColorButton string `json:"color_button,omitempty"`
|
||||||
|
ColorButtonText string `json:"color_button_text,omitempty"`
|
||||||
|
ColorCheckbox string `json:"color_checkbox,omitempty"`
|
||||||
|
ColorCheckboxCheckmark string `json:"color_checkbox_checkmark,omitempty"`
|
||||||
|
ColorHeader string `json:"color_header,omitempty"`
|
||||||
|
ColorLink string `json:"color_link,omitempty"`
|
||||||
|
DateOfBirthMandatory bool `json:"date_of_birth_mandatory,omitempty"`
|
||||||
|
ShippingDetails string `json:"shipping_details,omitempty"`
|
||||||
|
TitleMandatory bool `json:"title_mandatory,omitempty"`
|
||||||
|
AdditionalCheckbox *AdditionalCheckBox `json:"additional_checkbox"`
|
||||||
|
RadiusBorder string `json:"radius_border,omitempty"`
|
||||||
|
ShowSubtotalDetail bool `json:"show_subtotal_detail,omitempty"`
|
||||||
|
RequireValidateCallbackSuccess bool `json:"require_validate_callback_success,omitempty"`
|
||||||
|
AllowGlobalBillingCountries bool `json:"allow_global_billing_countries,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
AdditionalCheckBox struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Checked bool `json:"checked"`
|
||||||
|
Required bool `json:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckoutMerchantURLS struct {
|
||||||
|
// URL of merchant terms and conditions. Should be different than checkout, confirmation and push URLs.
|
||||||
|
// (max 2000 characters)
|
||||||
|
Terms string `json:"terms"`
|
||||||
|
|
||||||
|
// URL of merchant checkout page. Should be different than terms, confirmation and push URLs.
|
||||||
|
// (max 2000 characters)
|
||||||
|
Checkout string `json:"checkout"`
|
||||||
|
|
||||||
|
// URL of merchant confirmation page. Should be different than checkout and confirmation URLs.
|
||||||
|
// (max 2000 characters)
|
||||||
|
Confirmation string `json:"confirmation"`
|
||||||
|
|
||||||
|
// URL that will be requested when an order is completed. Should be different than checkout and
|
||||||
|
// confirmation URLs. (max 2000 characters)
|
||||||
|
Push string `json:"push"`
|
||||||
|
// URL that will be requested for final merchant validation. (must be https, max 2000 characters)
|
||||||
|
Validation string `json:"validation,omitempty"`
|
||||||
|
|
||||||
|
// URL for shipping option update. (must be https, max 2000 characters)
|
||||||
|
ShippingOptionUpdate string `json:"shipping_option_update,omitempty"`
|
||||||
|
|
||||||
|
// URL for shipping, tax and purchase currency updates. Will be called on address changes.
|
||||||
|
// (must be https, max 2000 characters)
|
||||||
|
AddressUpdate string `json:"address_update,omitempty"`
|
||||||
|
|
||||||
|
// URL for notifications on pending orders. (max 2000 characters)
|
||||||
|
Notification string `json:"notification,omitempty"`
|
||||||
|
|
||||||
|
// URL for shipping, tax and purchase currency updates. Will be called on purchase country changes.
|
||||||
|
// (must be https, max 2000 characters)
|
||||||
|
CountryChange string `json:"country_change,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckoutCustomer struct {
|
||||||
|
// DateOfBirth in string representation 2006-01-02
|
||||||
|
DateOfBirth string `json:"date_of_birth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address type define the address object (json serializable) being used for the API to represent billing &
|
||||||
|
// shipping addresses
|
||||||
|
Address struct {
|
||||||
|
GivenName string `json:"given_name,omitempty"`
|
||||||
|
FamilyName string `json:"family_name,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
StreetAddress string `json:"street_address,omitempty"`
|
||||||
|
StreetAddress2 string `json:"street_address2,omitempty"`
|
||||||
|
PostalCode string `json:"postal_code,omitempty"`
|
||||||
|
City string `json:"city,omitempty"`
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
Phone string `json:"phone,omitempty"`
|
||||||
|
Country string `json:"country,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Line struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Reference string `json:"reference,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
QuantityUnit string `json:"quantity_unit,omitempty"`
|
||||||
|
UnitPrice int `json:"unit_price"`
|
||||||
|
TaxRate int `json:"tax_rate"`
|
||||||
|
TotalAmount int `json:"total_amount"`
|
||||||
|
TotalDiscountAmount int `json:"total_discount_amount,omitempty"`
|
||||||
|
TotalTaxAmount int `json:"total_tax_amount"`
|
||||||
|
MerchantData string `json:"merchant_data,omitempty"`
|
||||||
|
ProductURL string `json:"product_url,omitempty"`
|
||||||
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
|
}
|
||||||
|
)
|
||||||
36
main.go
36
main.go
@@ -148,7 +148,9 @@ func main() {
|
|||||||
Url: amqpUrl,
|
Url: amqpUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
syncedServer := NewPoolServer(syncedPool, fmt.Sprintf("%s, %s", name, podIp))
|
klarnaClient := NewKlarnaClient(KlarnaPlaygroundUrl, os.Getenv("KLARNA_API_USERNAME"), os.Getenv("KLARNA_API_PASSWORD"))
|
||||||
|
|
||||||
|
syncedServer := NewPoolServer(syncedPool, fmt.Sprintf("%s, %s", name, podIp), klarnaClient)
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle("/cart/", http.StripPrefix("/cart", syncedServer.Serve()))
|
mux.Handle("/cart/", http.StripPrefix("/cart", syncedServer.Serve()))
|
||||||
// only for local
|
// only for local
|
||||||
@@ -194,7 +196,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
orderId := r.URL.Query().Get("order_id")
|
orderId := r.URL.Query().Get("order_id")
|
||||||
log.Printf("Order confirmation push: %s", orderId)
|
log.Printf("Order confirmation push: %s", orderId)
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.playground.klarna.com/checkout/v3/orders/%s", orderId), nil)
|
|
||||||
|
order, err := klarnaClient.GetOrder(orderId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error creating request: %v\n", err)
|
log.Printf("Error creating request: %v\n", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
@@ -202,27 +206,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
orderToSend, err := json.Marshal(order)
|
||||||
req.SetBasicAuth(APIUsername, APIPassword)
|
|
||||||
|
|
||||||
res, err := http.DefaultClient.Do(req)
|
|
||||||
|
|
||||||
if nil != err {
|
|
||||||
log.Printf("Error making request: %v\n", err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
var klarnaOrderResponse KlarnaOrderResponse
|
|
||||||
err = json.NewDecoder(res.Body).Decode(&klarnaOrderResponse)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error decoding response: %v\n", err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
orderToSend, err := json.Marshal(klarnaOrderResponse)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error marshaling order: %v\n", err)
|
log.Printf("Error marshaling order: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
@@ -242,11 +226,11 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = syncedServer.pool.Process(ToCartId(klarnaOrderResponse.MerchantReference1), Message{
|
_, err = syncedServer.pool.Process(ToCartId(order.MerchantReference1), Message{
|
||||||
Type: OrderCompletedType,
|
Type: OrderCompletedType,
|
||||||
Content: &messages.OrderCreated{
|
Content: &messages.OrderCreated{
|
||||||
OrderId: klarnaOrderResponse.OrderID,
|
OrderId: order.ID,
|
||||||
Status: klarnaOrderResponse.Status,
|
Status: order.Status,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,24 +5,25 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
messages "git.tornberg.me/go-cart-actor/proto"
|
messages "git.tornberg.me/go-cart-actor/proto"
|
||||||
"golang.org/x/exp/rand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PoolServer struct {
|
type PoolServer struct {
|
||||||
pod_name string
|
pod_name string
|
||||||
pool GrainPool
|
pool GrainPool
|
||||||
|
klarnaClient *KlarnaClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPoolServer(pool GrainPool, pod_name string) *PoolServer {
|
func NewPoolServer(pool GrainPool, pod_name string, klarnaClient *KlarnaClient) *PoolServer {
|
||||||
return &PoolServer{
|
return &PoolServer{
|
||||||
pod_name: pod_name,
|
pod_name: pod_name,
|
||||||
pool: pool,
|
pool: pool,
|
||||||
|
klarnaClient: klarnaClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,38 +216,23 @@ func (s *PoolServer) HandleAddRequest(w http.ResponseWriter, r *http.Request, id
|
|||||||
return s.WriteResult(w, reply)
|
return s.WriteResult(w, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
APIUsername = os.Getenv("KLARNA_API_USERNAME")
|
|
||||||
APIPassword = os.Getenv("KLARNA_API_PASSWORD")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *PoolServer) HandleConfirmation(w http.ResponseWriter, r *http.Request, id CartId) error {
|
func (s *PoolServer) HandleConfirmation(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||||
orderId := r.PathValue("orderId")
|
orderId := r.PathValue("orderId")
|
||||||
if orderId == "" {
|
if orderId == "" {
|
||||||
return fmt.Errorf("orderId is empty")
|
return fmt.Errorf("orderId is empty")
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.playground.klarna.com/checkout/v3/orders/%s", orderId), nil)
|
order, err := s.klarnaClient.GetOrder(orderId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
req.SetBasicAuth(APIUsername, APIPassword)
|
|
||||||
|
|
||||||
res, err := http.DefaultClient.Do(req)
|
|
||||||
if nil != err {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(res.Body)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Header().Set("X-Pod-Name", s.pod_name)
|
w.Header().Set("X-Pod-Name", s.pod_name)
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
w.WriteHeader(res.StatusCode)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
return json.NewEncoder(w).Encode(order)
|
||||||
w.Write(buf.Bytes())
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PoolServer) HandleCheckout(w http.ResponseWriter, r *http.Request, id CartId) error {
|
func (s *PoolServer) HandleCheckout(w http.ResponseWriter, r *http.Request, id CartId) error {
|
||||||
@@ -267,29 +253,19 @@ func (s *PoolServer) HandleCheckout(w http.ResponseWriter, r *http.Request, id C
|
|||||||
return s.WriteResult(w, reply)
|
return s.WriteResult(w, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "https://api.playground.klarna.com/checkout/v3/orders", bytes.NewReader(reply.Payload))
|
order, err := s.klarnaClient.CreateOrder(bytes.NewReader(reply.Payload))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
req.SetBasicAuth(APIUsername, APIPassword)
|
|
||||||
|
|
||||||
res, err := http.DefaultClient.Do(req)
|
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
buf.ReadFrom(res.Body)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Header().Set("X-Pod-Name", s.pod_name)
|
w.Header().Set("X-Pod-Name", s.pod_name)
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
w.WriteHeader(res.StatusCode)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
w.Write(buf.Bytes())
|
return json.NewEncoder(w).Encode(order)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCartId() CartId {
|
func NewCartId() CartId {
|
||||||
|
|||||||
Reference in New Issue
Block a user