Skip to content

Webhooks & Signature Verification

Shopier dispatches event notifications via HTTP POST to your configured Notification URL. To guarantee authenticity and prevent tampering, every webhook payload is signed with HS256 (HMAC-SHA256) using your app's webhook secret token.


Webhook Headers

HeaderDescription
Shopier-SignatureHMAC-SHA256 signature string (hex or base64)
Shopier-EventEvent type identifier (e.g., order.created)
Shopier-Webhook-IdUnique ID of the notification event
Shopier-TimestampUnix timestamp in seconds (UTC)
Shopier-Account-IdThe shop account ID associated with the event
Shopier-Api-VersionAPI version string

Supported Events

Event ConstantEvent NamePayload ModelTrigger Scenario
webhook.EventOrderCreatedorder.created*shopier.OrderA new order is paid and placed
webhook.EventOrderAddressUpdatedorder.addressUpdated*shopier.OrderBuyer shipping address is updated
webhook.EventOrderFulfilledorder.fulfilled*shopier.OrderOrder is closed and marked shipped
webhook.EventProductCreatedproduct.created*shopier.ProductA new product listing is published
webhook.EventProductUpdatedproduct.updated*shopier.ProductAn existing product is updated
webhook.EventRefundRequestedrefund.requested*shopier.RefundA seller requests an order refund
webhook.EventRefundUpdatedrefund.updated*shopier.RefundRefund succeeds or fails

1. Using the Built-in http.Handler Adapter

The fastest way to mount a production-ready webhook endpoint in Go:

go
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/AdisGroup/shopier-go/webhook"
)

func main() {
	secret := os.Getenv("SHOPIER_WEBHOOK_SECRET")

	handler := webhook.NewHandler(secret, func(ctx context.Context, evt *webhook.Event) error {
		log.Printf("Received event: %s (ID: %s)", evt.Header.Event, evt.Header.WebhookID)

		switch evt.Header.Event {
		case webhook.EventOrderCreated:
			order, err := evt.Order()
			if err != nil {
				return err
			}
			fmt.Printf("New Order #%s for %s %s\n", order.ID, order.Totals.Total, order.Currency)

		case webhook.EventRefundRequested:
			refund, err := evt.Refund()
			if err != nil {
				return err
			}
			fmt.Printf("Refund requested: #%s (Order: %s)\n", refund.ID, refund.OrderID)
		}

		return nil
	})

	http.Handle("/webhooks/shopier", handler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

2. Manual Verification in Web Frameworks (Gin, Chi, Fiber)

Standard webhook.Parse

You can verify and parse any *http.Request directly:

go
event, err := webhook.Parse(r, webhookSecret)
if err != nil {
	http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized)
	return
}

order, _ := event.Order()

Direct Byte Verification (webhook.VerifySignature)

For frameworks like Fiber that do not expose standard *http.Request:

go
// Fiber example
app.Post("/webhooks/shopier", func(c *fiber.Ctx) error {
	sig := c.Get("Shopier-Signature")
	body := c.Body()

	if !webhook.VerifySignature(body, sig, webhookSecret) {
		return c.Status(fiber.StatusUnauthorized).SendString("Invalid signature")
	}

	// Process payload...
	return c.SendStatus(fiber.StatusOK)
})