added google sheets
This commit is contained in:
parent
37ca207b92
commit
c4b06eb6c4
7 changed files with 304 additions and 28 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,3 +3,4 @@ www
|
||||||
*_templ.go
|
*_templ.go
|
||||||
piekarnikgosi
|
piekarnikgosi
|
||||||
.env
|
.env
|
||||||
|
key.json
|
||||||
|
|
|
||||||
28
dev.sh
28
dev.sh
|
|
@ -1,29 +1,35 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
exec 3< <(stripe listen --events checkout.session.completed,checkout.session.async_payment_succeeded --forward-to localhost:8000/webhook 2>&1)
|
LOG_FILE=$(mktemp)
|
||||||
CHILD_PID=$!
|
trap "rm -f $LOG_FILE; kill $(jobs -p) 2>/dev/null" EXIT
|
||||||
trap "kill -TERM $CHILD_PID 2>/dev/null; exec 3<&-" EXIT
|
|
||||||
|
|
||||||
if [ -f .env ]; then
|
if [ -f .env ]; then
|
||||||
export $(cat .env | xargs)
|
export $(cat .env | xargs)
|
||||||
fi
|
fi
|
||||||
export DEV_ENV="TRUE"
|
export DEV_ENV="TRUE"
|
||||||
export WEBHOOK_SECRET=""
|
|
||||||
|
|
||||||
while IFS= read -r line <&3; do
|
echo "Starting Stripe CLI listener..."
|
||||||
matched_value=$(echo "$line" | grep -oP 'whsec_[a-zA-Z0-9]+')
|
|
||||||
if [ -n "$matched_value" ]; then
|
stripe listen --events checkout.session.completed,checkout.session.async_payment_succeeded --forward-to localhost:8000/webhook > "$LOG_FILE" 2>&1 &
|
||||||
WEBHOOK_SECRET=$matched_value
|
|
||||||
|
WEBHOOK_SECRET=""
|
||||||
|
for i in {1..10}; do
|
||||||
|
if grep -q "whsec_" "$LOG_FILE"; then
|
||||||
|
# Extract the exact whsec_ token
|
||||||
|
WEBHOOK_SECRET=$(grep -oE 'whsec_[a-zA-Z0-9]+' "$LOG_FILE" | head -n 1)
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
sleep 0.5
|
||||||
done
|
done
|
||||||
|
|
||||||
exec 3<&-
|
|
||||||
|
|
||||||
if [ -z "$WEBHOOK_SECRET" ]; then
|
if [ -z "$WEBHOOK_SECRET" ]; then
|
||||||
echo "Error: can't get the webhook secret"
|
echo "Error: can't get the webhook secret from Stripe CLI"
|
||||||
|
cat "$LOG_FILE"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Successfully captured webhook secret: $WEBHOOK_SECRET"
|
||||||
|
export WEBHOOK_SECRET
|
||||||
|
|
||||||
go tool templ generate
|
go tool templ generate
|
||||||
go run .
|
go run .
|
||||||
|
|
|
||||||
34
go.mod
34
go.mod
|
|
@ -10,21 +10,45 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go/auth v0.24.1-0.20261001053825-dbc26066f70a // indirect
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.3.1 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.10.0 // indirect
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cli/browser v1.3.0 // indirect
|
github.com/cli/browser v1.3.0 // indirect
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.4 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.11 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.24 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.27.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/natefinch/atomic v1.0.1 // indirect
|
github.com/natefinch/atomic v1.0.1 // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.8.12 // indirect
|
github.com/tdewolff/parse/v2 v2.8.12 // indirect
|
||||||
golang.org/x/mod v0.26.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
golang.org/x/net v0.51.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
go.opentelemetry.io/otel v1.47.0 // indirect
|
||||||
golang.org/x/sys v0.43.0 // indirect
|
go.opentelemetry.io/otel/log v1.47.0 // indirect
|
||||||
golang.org/x/tools v0.35.0 // indirect
|
go.opentelemetry.io/otel/metric v1.47.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.47.0 // indirect
|
||||||
|
golang.org/x/crypto v0.58.0 // indirect
|
||||||
|
golang.org/x/mod v0.42.0 // indirect
|
||||||
|
golang.org/x/net v0.62.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.37.0 // indirect
|
||||||
|
golang.org/x/sync v0.24.0 // indirect
|
||||||
|
golang.org/x/sys v0.49.0 // indirect
|
||||||
|
golang.org/x/text v0.43.0 // indirect
|
||||||
|
golang.org/x/tools v0.51.0 // indirect
|
||||||
|
google.golang.org/api v0.311.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e // indirect
|
||||||
|
google.golang.org/grpc v1.85.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.12 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
tool github.com/a-h/templ/cmd/templ
|
tool github.com/a-h/templ/cmd/templ
|
||||||
|
|
|
||||||
60
go.sum
60
go.sum
|
|
@ -1,3 +1,9 @@
|
||||||
|
cloud.google.com/go/auth v0.24.1-0.20261001053825-dbc26066f70a h1:jW1ZLFy9oa5ai8LOI40z6s+DL1cO/L1KSm9hVGnibn8=
|
||||||
|
cloud.google.com/go/auth v0.24.1-0.20261001053825-dbc26066f70a/go.mod h1:1dmBXa9xfBvBr39ZnDUDkaaA91ccq9CcqENDgJA4d7c=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.3.1 h1:BKLDdbWS7pafd820dsyFDF6fPEzIGqr7ny6hbTzCCgE=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.3.1/go.mod h1:B1nM4FJDxoDqAt3PXFG4IflUoQ5kvWAOOM3R+5wAszg=
|
||||||
|
cloud.google.com/go/compute/metadata v0.10.0 h1:pyKMUQSwchgkIBBJGdILqQbs/BNJXqwSA7Ej6LAvvtY=
|
||||||
|
cloud.google.com/go/compute/metadata v0.10.0/go.mod h1:rGFHRrIif570kSibjFTMbt6/4/tzgJWFGI/HVol4GIk=
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
|
||||||
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
||||||
|
|
@ -6,16 +12,34 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
|
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
|
||||||
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
|
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
|
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||||
|
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||||
|
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/s2a-go v0.1.11 h1:7v1V1TraNGP63WptsAkNqr2w8jgm4vM6A6qx6Lsw9iM=
|
||||||
|
github.com/google/s2a-go v0.1.11/go.mod h1:pz4tyvwXvJLLbyrkh6FW1eS2zPUXMaTmyNhYtyP2tNw=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.24 h1:G9iCJIY+AYVmUskKdjX8FVy7sG+nbzjOQ41cvlmhcvg=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.24/go.mod h1:hEjCmnoakQzv5ImLUp6alkY7XgLlNoAxwTXmhHHNIsI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.27.0 h1:qe2iQOUzxb3fd6x6ewpIEixPQhJ+0Ihc/RRZhKA/2g0=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.27.0/go.mod h1:RofiqDw3ehcG4L0H1zO3D2Itpwd3/uS/7wdmbNDF1DE=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
|
@ -36,21 +60,57 @@ github.com/tdewolff/parse/v2 v2.8.12/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLB
|
||||||
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||||
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
||||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
|
||||||
|
go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM=
|
||||||
|
go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU=
|
||||||
|
go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI=
|
||||||
|
go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw=
|
||||||
|
go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY=
|
||||||
|
go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU=
|
||||||
|
go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM=
|
||||||
|
go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0=
|
||||||
|
golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54=
|
||||||
|
golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w=
|
||||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
|
golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg=
|
||||||
|
golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs=
|
||||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
|
golang.org/x/net v0.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY=
|
||||||
|
golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
|
||||||
|
golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
|
||||||
|
golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||||
|
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
|
||||||
|
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
|
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
|
||||||
|
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
|
||||||
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
||||||
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
|
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
|
||||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||||
|
golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=
|
||||||
|
golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA=
|
||||||
|
google.golang.org/api v0.311.0 h1:t9xGmC/+3kC75YmJldWrlVW2EZqS+PhxL0QMTdytlAY=
|
||||||
|
google.golang.org/api v0.311.0/go.mod h1:x9+Bc6l3cH1HItpSGqV2mZnbM1bk1VmpLJMwKEtsmgg=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e h1:w3fy/G8OrJHYexn+u5y7hFyc8YRRIr7DCevtIOwAcGY=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
|
||||||
|
google.golang.org/grpc v1.85.0 h1:BPBCfuVRQ1qvBhKDOq9uvQllMvO59rKsYhXpp7Dd8vw=
|
||||||
|
google.golang.org/grpc v1.85.0/go.mod h1:0rQrOD58yjal/0EaVbGBANindixsCZNQPDV3VKtaKuQ=
|
||||||
|
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||||
|
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ func parseOrder(r *http.Request) (order, error) {
|
||||||
if total < 500 {
|
if total < 500 {
|
||||||
errorList = append(errorList, fmt.Errorf("Minimalna wartość zamówienia wynosi 50zł"))
|
errorList = append(errorList, fmt.Errorf("Minimalna wartość zamówienia wynosi 50zł"))
|
||||||
}
|
}
|
||||||
o.id = newOrderID()
|
|
||||||
return o, errors.Join(errorList...)
|
return o, errors.Join(errorList...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,5 +78,10 @@ func OrderHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
writeErrors(w, r, http.StatusUnprocessableEntity, errorMessages(err)...)
|
writeErrors(w, r, http.StatusUnprocessableEntity, errorMessages(err)...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
o.id, err = insertPending(o)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ORDER_SHEETS_ERROR", err)
|
||||||
|
o.id = newOrderID()
|
||||||
|
}
|
||||||
startCheckout(w, r, o)
|
startCheckout(w, r, o)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
166
orders/sheets.go
Normal file
166
orders/sheets.go
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
package orders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/api/option"
|
||||||
|
"google.golang.org/api/sheets/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tab = "Zamówienia"
|
||||||
|
statusPending = "oczekuje"
|
||||||
|
statusConfirmed = "potwierdzone"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
sheetsSvc *sheets.Service
|
||||||
|
SheetID string
|
||||||
|
rowMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SheetID = os.Getenv("SHEET_ID")
|
||||||
|
credsFile := os.Getenv("SHEETS_CREDS")
|
||||||
|
var err error
|
||||||
|
sheetsSvc, err = sheets.NewService(context.Background(), option.WithCredentialsFile(credsFile))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("init google sheets")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sctx() (context.Context, context.CancelFunc) {
|
||||||
|
return context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cell(r []any, i int) string {
|
||||||
|
if i < len(r) && r[i] != nil {
|
||||||
|
return strings.TrimSpace(fmt.Sprint(r[i]))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(ctx context.Context, a1 string) ([][]any, error) {
|
||||||
|
res, err := sheetsSvc.Spreadsheets.Values.Get(SheetID, a1).Context(ctx).Do()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearRow(ctx context.Context, row int) error {
|
||||||
|
_, err := sheetsSvc.Spreadsheets.Values.BatchClear(SheetID, &sheets.BatchClearValuesRequest{
|
||||||
|
Ranges: []string{
|
||||||
|
fmt.Sprintf("%s!B%d:D%d", tab, row, row),
|
||||||
|
fmt.Sprintf("%s!G%d", tab, row),
|
||||||
|
fmt.Sprintf("%s!I%d:T%d", tab, row, row),
|
||||||
|
},
|
||||||
|
}).Context(ctx).Do()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertPending(o order) (string, error) {
|
||||||
|
ctx, cancel := sctx()
|
||||||
|
defer cancel()
|
||||||
|
rowMu.Lock()
|
||||||
|
defer rowMu.Unlock()
|
||||||
|
|
||||||
|
vals, err := get(ctx, tab+"!B:C")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
row := max(len(vals)+1, 4)
|
||||||
|
for i := 3; i < len(vals); i++ {
|
||||||
|
if cell(vals[i], 0) == "" && cell(vals[i], 1) == "" {
|
||||||
|
row = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []*sheets.ValueRange{{
|
||||||
|
Range: fmt.Sprintf("%s!B%d:D%d", tab, row, row),
|
||||||
|
Values: [][]any{{o.phone, o.name, statusPending}},
|
||||||
|
}}
|
||||||
|
for _, it := range o.items {
|
||||||
|
if it.count > 0 {
|
||||||
|
data = append(data, &sheets.ValueRange{
|
||||||
|
Range: fmt.Sprintf("%s!%s%d", tab, it.id, row),
|
||||||
|
Values: [][]any{{float64(it.count) / 10}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = sheetsSvc.Spreadsheets.Values.BatchUpdate(SheetID, &sheets.BatchUpdateValuesRequest{
|
||||||
|
ValueInputOption: "RAW",
|
||||||
|
Data: data,
|
||||||
|
}).Context(ctx).Do()
|
||||||
|
if err != nil {
|
||||||
|
for _, datum := range data {
|
||||||
|
log.Println(datum)
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
idVals, err := get(ctx, fmt.Sprintf("%s!A%d", tab, row))
|
||||||
|
if err != nil || len(idVals) == 0 || cell(idVals[0], 0) == "" {
|
||||||
|
clearRow(ctx, row)
|
||||||
|
return "", errors.New("row has no id")
|
||||||
|
}
|
||||||
|
return cell(idVals[0], 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPendingRow(ctx context.Context, id string) (int, error) {
|
||||||
|
ids, err := get(ctx, tab+"!A:A")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for i, r := range ids {
|
||||||
|
if i == 0 || cell(r, 0) != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
row := i + 1
|
||||||
|
v, err := get(ctx, fmt.Sprintf("%s!D%d", tab, row))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(v) > 0 && cell(v[0], 0) == statusPending {
|
||||||
|
return row, nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("row is not pending")
|
||||||
|
}
|
||||||
|
return 0, errors.New("no row with this id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirmRow(id string) error {
|
||||||
|
ctx, cancel := sctx()
|
||||||
|
defer cancel()
|
||||||
|
row, err := findPendingRow(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = sheetsSvc.Spreadsheets.Values.BatchUpdate(SheetID, &sheets.BatchUpdateValuesRequest{
|
||||||
|
ValueInputOption: "RAW",
|
||||||
|
Data: []*sheets.ValueRange{
|
||||||
|
{Range: fmt.Sprintf("%s!D%d", tab, row), Values: [][]any{{statusConfirmed}}},
|
||||||
|
{Range: fmt.Sprintf("%s!G%d", tab, row), Values: [][]any{{float64(retainer) / 100}}},
|
||||||
|
},
|
||||||
|
}).Context(ctx).Do()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearOrder(id string) error {
|
||||||
|
ctx, cancel := sctx()
|
||||||
|
defer cancel()
|
||||||
|
row, err := findPendingRow(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return clearRow(ctx, row)
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stripe/stripe-go/v82"
|
"github.com/stripe/stripe-go/v82"
|
||||||
"github.com/stripe/stripe-go/v82/checkout/session"
|
"github.com/stripe/stripe-go/v82/checkout/session"
|
||||||
|
|
@ -24,6 +25,8 @@ func init() {
|
||||||
log.Println("the url is: ", siteUrl)
|
log.Println("the url is: ", siteUrl)
|
||||||
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
|
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
|
||||||
webhookSecret = os.Getenv("WEBHOOK_SECRET")
|
webhookSecret = os.Getenv("WEBHOOK_SECRET")
|
||||||
|
log.Println(stripe.Key)
|
||||||
|
log.Println(webhookSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCheckout(w http.ResponseWriter, r *http.Request, o order) {
|
func startCheckout(w http.ResponseWriter, r *http.Request, o order) {
|
||||||
|
|
@ -36,13 +39,14 @@ func startCheckout(w http.ResponseWriter, r *http.Request, o order) {
|
||||||
Mode: stripe.String("payment"),
|
Mode: stripe.String("payment"),
|
||||||
ClientReferenceID: stripe.String(o.id),
|
ClientReferenceID: stripe.String(o.id),
|
||||||
Locale: stripe.String("pl"),
|
Locale: stripe.String("pl"),
|
||||||
|
ExpiresAt: stripe.Int64(time.Now().Add(time.Hour).Unix()),
|
||||||
LineItems: []*stripe.CheckoutSessionLineItemParams{{
|
LineItems: []*stripe.CheckoutSessionLineItemParams{{
|
||||||
Quantity: stripe.Int64(1),
|
Quantity: stripe.Int64(1),
|
||||||
PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{
|
PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{
|
||||||
Currency: stripe.String("pln"),
|
Currency: stripe.String("pln"),
|
||||||
UnitAmount: stripe.Int64(retainer),
|
UnitAmount: stripe.Int64(retainer),
|
||||||
ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{
|
ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{
|
||||||
Name: stripe.String("Zaliczka na zamówienie " + o.id),
|
Name: stripe.String("Zaliczka na zamówienie nr " + o.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
|
|
@ -75,7 +79,10 @@ func WebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.Type != "checkout.session.completed" && event.Type != "checkout.session.async_payment_succeeded" {
|
switch event.Type {
|
||||||
|
case "checkout.session.completed", "checkout.session.async_payment_succeeded",
|
||||||
|
"checkout.session.expired", "checkout.session.async_payment_failed":
|
||||||
|
default:
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -86,14 +93,22 @@ func WebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
id := s.ClientReferenceID
|
||||||
case s.PaymentStatus != stripe.CheckoutSessionPaymentStatusPaid:
|
|
||||||
log.Println("ORDER_NOT_YET_PAID", s.ClientReferenceID, s.ID, s.PaymentStatus)
|
|
||||||
case s.AmountTotal != retainer || s.Currency != stripe.CurrencyPLN:
|
|
||||||
log.Println("ORDER_PAYMENT_MISMATCH", s.ClientReferenceID, s.ID, s.AmountTotal, s.Currency)
|
|
||||||
default:
|
|
||||||
log.Println("ORDER_PAID", s.ClientReferenceID, s.ID)
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case event.Type == "checkout.session.expired" || event.Type == "checkout.session.async_payment_failed":
|
||||||
|
log.Println("ORDER_ABANDONED", id, event.Type)
|
||||||
|
if err := clearOrder(id); err != nil {
|
||||||
|
log.Println("ORDER_SHEETS_ERROR", id, err)
|
||||||
|
}
|
||||||
|
case s.PaymentStatus != stripe.CheckoutSessionPaymentStatusPaid:
|
||||||
|
log.Println("ORDER_NOT_YET_PAID", id, s.ID, s.PaymentStatus)
|
||||||
|
case s.AmountTotal != retainer || s.Currency != stripe.CurrencyPLN:
|
||||||
|
log.Println("ORDER_PAYMENT_MISMATCH", id, s.ID, s.AmountTotal, s.Currency)
|
||||||
|
default:
|
||||||
|
log.Println("ORDER_PAID", id, s.ID)
|
||||||
|
if err := confirmRow(id); err != nil {
|
||||||
|
log.Println("ORDER_SHEETS_ERROR", id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue