mirror of
https://github.com/marcusDenslow/terminalShop.git
synced 2026-10-25 10:02:21 +01:00
feat(notify): post Slack message when an order is paid
This commit is contained in:
@@ -5,6 +5,7 @@ package audit
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"terminalShop/pkg/notify"
|
||||
)
|
||||
|
||||
type eventType string
|
||||
@@ -116,6 +117,7 @@ func OrderPaid(userID, orderID uint, amountCents int, stripePaymentIntentID stri
|
||||
Amount: amountCents,
|
||||
StripeID: stripePaymentIntentID,
|
||||
}.attrs()...)
|
||||
go notify.SlackOrderPaid(orderID, amountCents)
|
||||
}
|
||||
|
||||
// OrderShipped records that an order has been marked as shipped with carrier metadata
|
||||
|
||||
33
pkg/notify/slack.go
Normal file
33
pkg/notify/slack.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var slackClient = &http.Client{Timeout: 5 * time.Second}
|
||||
|
||||
func SlackOrderPaid(orderID uint, amountCents int) {
|
||||
url := os.Getenv("SLACK_WEBHOOK_URL")
|
||||
if url == "" {
|
||||
return
|
||||
}
|
||||
|
||||
text := fmt.Sprintf(":coffee: Order #%d paid - $%.2f", orderID, float64(amountCents)/100)
|
||||
body, _ := json.Marshal(map[string]string{"text": text})
|
||||
|
||||
resp, err := slackClient.Post(url, "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
slog.Warn("slack notify failed", "error", err, "order_id", orderID)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 300 {
|
||||
slog.Warn("slack notify non-2xx", "status", resp.StatusCode, "order_id", orderID)
|
||||
}
|
||||
}
|
||||
12
scripts/ops/buy-label.sh
Executable file
12
scripts/ops/buy-label.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Buy a shipping label via the admin endpoint.
|
||||
# Usage: scripts/ops/buy-label.sh <order_id>
|
||||
set -euo pipefail
|
||||
|
||||
ORDER_ID="${1:?usage: $0 <order_id>}"
|
||||
: "${ADMIN_KEY:?ADMIN_KEY not set; source scripts/ops/env.sh first}"
|
||||
: "${BASE_URL:=https://api.sshops.uk}"
|
||||
|
||||
curl -sS -X POST "$BASE_URL/api/v1/orders/$ORDER_ID/label" \
|
||||
-H "X-Admin-Key: $ADMIN_KEY" \
|
||||
-H "Content-Type: application/json" | jq .
|
||||
19
scripts/ops/env.sh
Executable file
19
scripts/ops/env.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# Source once per shell session: `source scripts/ops/env.sh`
|
||||
# Fetches ADMIN_API_KEY + SHIPPO_WEBHOOK_SECRET from prod's .env.prod
|
||||
# and exports them so the other ops scripts can use them.
|
||||
|
||||
export BASE_URL="${BASE_URL:-https://api.sshops.uk}"
|
||||
|
||||
ADMIN_KEY_FETCHED=$(ssh ubuntu-helsinki "grep '^ADMIN_API_KEY=' /root/terminalShop/.env.prod | cut -d= -f2-")
|
||||
SECRET_FETCHED=$(ssh ubuntu-helsinki "grep '^SHIPPO_WEBHOOK_SECRET=' /root/terminalShop/.env.prod | cut -d= -f2-")
|
||||
|
||||
if [[ -z "$ADMIN_KEY_FETCHED" || -z "$SECRET_FETCHED" ]]; then
|
||||
echo "failed to fetch secrets from prod; aborting" >&2
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
export ADMIN_KEY="$ADMIN_KEY_FETCHED"
|
||||
export SECRET="$SECRET_FETCHED"
|
||||
|
||||
echo "ops env loaded (BASE_URL=$BASE_URL)"
|
||||
6
scripts/ops/list-paid.sh
Executable file
6
scripts/ops/list-paid.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# List recent paid orders awaiting label purchase.
|
||||
# Usage: scripts/ops/list-paid.sh
|
||||
set -euo pipefail
|
||||
|
||||
ssh ubuntu-helsinki "sqlite3 -header -column /root/terminalShop/data/terminalshop.db \"SELECT id, status, total, shipping_name, shipping_city, shipping_country FROM orders WHERE status='paid' ORDER BY id DESC LIMIT 10;\""
|
||||
8
scripts/ops/order-state.sh
Executable file
8
scripts/ops/order-state.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# Show the current DB row state for an order (lifecycle + tracking columns).
|
||||
# Usage: scripts/ops/order-state.sh <order_id>
|
||||
set -euo pipefail
|
||||
|
||||
ORDER_ID="${1:?usage: $0 <order_id>}"
|
||||
|
||||
ssh ubuntu-helsinki "sqlite3 -header -column /root/terminalShop/data/terminalshop.db \"SELECT id, status, total, carrier, tracking_number, tracking_status, tracking_status_details, tracking_status_updated_at FROM orders WHERE id=$ORDER_ID;\""
|
||||
58
scripts/ops/scan.sh
Executable file
58
scripts/ops/scan.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fire a Shippo-shaped tracking webhook against an order's tracking number.
|
||||
# Usage: scripts/ops/scan.sh <order_id> <pre_transit|transit|delivered|failure|returned> [--detail "free text"]
|
||||
set -euo pipefail
|
||||
|
||||
ORDER_ID="${1:?usage: $0 <order_id> <status> [--detail \"text\"]}"
|
||||
STATUS_LOWER="${2:?missing status; one of: pre_transit transit delivered failure returned}"
|
||||
DETAIL=""
|
||||
if [[ "${3:-}" == "--detail" ]]; then
|
||||
DETAIL="${4:?--detail requires a value}"
|
||||
fi
|
||||
|
||||
: "${SECRET:?SECRET not set; source scripts/ops/env.sh first}"
|
||||
: "${BASE_URL:=https://api.sshops.uk}"
|
||||
|
||||
case "$STATUS_LOWER" in
|
||||
pre_transit) STATUS="PRE_TRANSIT" ;;
|
||||
transit) STATUS="TRANSIT" ;;
|
||||
delivered) STATUS="DELIVERED" ;;
|
||||
failure) STATUS="FAILURE" ;;
|
||||
returned) STATUS="RETURNED" ;;
|
||||
*) echo "unknown status: $STATUS_LOWER" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
if [[ -z "$DETAIL" ]]; then
|
||||
case "$STATUS" in
|
||||
PRE_TRANSIT) DETAIL="Shipping label created, USPS awaiting item" ;;
|
||||
TRANSIT) DETAIL="In transit to destination" ;;
|
||||
DELIVERED) DETAIL="Delivered, front porch" ;;
|
||||
FAILURE) DETAIL="Delivery exception" ;;
|
||||
RETURNED) DETAIL="Returned to sender" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
TRACKING=$(ssh ubuntu-helsinki "sqlite3 /root/terminalShop/data/terminalshop.db \"SELECT tracking_number FROM orders WHERE id=$ORDER_ID;\"")
|
||||
if [[ -z "$TRACKING" ]]; then
|
||||
echo "order $ORDER_ID has no tracking number; buy a label first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATUS_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
curl -sS -X POST "$BASE_URL/api/v1/webhooks/shippo?token=$SECRET" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data @- <<EOF | jq .
|
||||
{
|
||||
"event": "track_updated",
|
||||
"data": {
|
||||
"tracking_number": "$TRACKING",
|
||||
"carrier": "usps",
|
||||
"tracking_status": {
|
||||
"status": "$STATUS",
|
||||
"status_date": "$STATUS_DATE",
|
||||
"status_details": "$DETAIL"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
Reference in New Issue
Block a user