Skip to content

piusalfred/whatsapp

Repository files navigation

whatsapp

GoDoc Go Report Card Status

A highly configurable golang client for Whatsapp Cloud API

Warning

The library is usable but not pretty stable yet. There maybe some breaking changes.

Supported API

setup

  • Go to apps you have registered click on the one you want to develop API for and go to API setup if not create one.
  • Get these details access token,phone number id,business account id and make sure you have authorized one or more whatsapp phone numbers to receive these messages
  • While on the root directory of the project run task build-examples to build all the examples that will be in examples/bin directory
  • Then run cp examples/api.env examples/bin/api.env
  • And populate the values you have got to this api.env file, The given values here are not valid
WHATSAPP_CLOUD_API_BASE_URL=https://graph.facebook.com
WHATSAPP_CLOUD_API_API_VERSION=v20.0
WHATSAPP_CLOUD_API_ACCESS_TOKEN=EAALLrT0ok6UBJZB0ZB3gvzk9hJaEjGM8ISZAxPR5e3ZAFn4RmBIThoeK0XOdbKv8y2zB3YQ7uaijShZBjVIcZD
WHATSAPP_CLOUD_API_PHONE_NUMBER_ID=111112271552333
WHATSAPP_CLOUD_API_BUSINESS_ACCOUNT_ID=222222508300000
WHATSAPP_CLOUD_API_TEST_RECIPIENT=+XXXX7374665453
  • now you can navigate to the examples/bin directory and run the examples. But before that make sure you have sent the hello world template message to the recipient and replied and this should be within 24 hrs window

Note

You will find BaseClient and Client. Client provides a stateful approach, reusing the same configuration across multiple requests until manually refreshed, making it ideal for long-running services where consistency and thread safety are required. BaseClient is stateless, reloading the configuration on each request, making it more flexible for dynamic environments like multi-tenancy, where different configurations may be needed for each request.

usage

Install the library by running

go get github.com/piusalfred/whatsapp
package main

import (
	"context"
	"fmt"
	whttp "github.com/piusalfred/whatsapp/pkg/http"
	"log"
	"net/http"

	"github.com/piusalfred/whatsapp/config"
	"github.com/piusalfred/whatsapp/message"
)

func main() {
	// Create the HTTP client
	httpClient := &http.Client{}

	// Define a configuration reader function
	configReader := config.ReaderFunc(func(ctx context.Context) (*config.Config, error) {
		return &config.Config{
			BaseURL:           "https://your-whatsapp-api-url",   // Replace with your API URL
			AccessToken:       "your-access-token",              // Replace with your access token
			APIVersion:        "v14.0",                          // WhatsApp API version
			BusinessAccountID: "your-business-account-id",       // Replace with your business account ID
		}, nil
	})

	clientOptions := []whttp.CoreClientOption[message.Message]{
		whttp.WithCoreClientHTTPClient[message.Message](httpClient),
		whttp.WithCoreClientRequestInterceptor[message.Message](
			func(ctx context.Context, req *http.Request) error {
				fmt.Println("Request Intercepted")
				return nil
			},
		),
		whttp.WithCoreClientResponseInterceptor[message.Message](
			func(ctx context.Context, resp *http.Response) error {
				fmt.Println("Response Intercepted")
				return nil
			},
		),
	}

	coreClient := whttp.NewSender[message.Message](clientOptions...)
	client, err := message.NewBaseClient(coreClient, configReader)
	if err != nil {
		log.Fatalf("failed to create client: %v", err)
	}

	// Define the recipient's WhatsApp phone number (including country code)
	recipient := "1234567890"

	// Create a new text message request
	textMessage := message.NewRequest(recipient, &message.Text{
		Body: "Hello, this is a test message!",
	}, "")

	// Send the text message
	ctx := context.Background()
	response, err := client.SendText(ctx, textMessage)
	if err != nil {
		log.Fatalf("failed to send message: %v", err)
	}

	// Print the response from the API
	fmt.Printf("Message sent successfully! Response: %+v\n", response)
}

webhooks

package main

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

	"github.com/piusalfred/whatsapp/webhooks/flow"

	"github.com/piusalfred/whatsapp/webhooks"
	"github.com/piusalfred/whatsapp/webhooks/business"
	"github.com/piusalfred/whatsapp/webhooks/message"
)

func HandleBusinessNotification(ctx context.Context, notification *business.Notification) *webhooks.Response {
	fmt.Printf("Business notification received: %+v\n", notification)
	return &webhooks.Response{StatusCode: http.StatusOK}
}

func HandleMessageNotification(ctx context.Context, notification *message.Notification) *webhooks.Response {
	fmt.Printf("Message notification received: %+v\n", notification)
	return &webhooks.Response{StatusCode: http.StatusOK}
}

func HandleFlowNotification(ctx context.Context, notification *flow.Notification) *webhooks.Response {
	fmt.Printf("Message notification received: %+v\n", notification)
	return &webhooks.Response{StatusCode: http.StatusOK}
}

func main() {
	messageWebhooksHandler := webhooks.OnEventNotification(
		message.NotificationHandlerFunc(
			HandleMessageNotification,
		),
	)

	businessWebhooksHandler := webhooks.OnEventNotification(
		business.NotificationHandlerFunc(
			HandleBusinessNotification,
		),
	)

	flowEventsHandler := webhooks.OnEventNotification(
		flow.NotificationHandlerFunc(HandleFlowNotification),
	)

	messageVerifyToken := webhooks.VerifyTokenReader(func(ctx context.Context) (string, error) {
		return "message-subscription-token", nil
	})

	businessVerifyToken := webhooks.VerifyTokenReader(func(ctx context.Context) (string, error) {
		return "business-subscription-token", nil
	})

	validatorWrapper := func(next http.HandlerFunc) http.HandlerFunc {
		fn := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			if err := webhooks.ValidateRequestPayloadSignature(request, "validate-secret"); err != nil {
				http.Error(writer, err.Error(), http.StatusInternalServerError)

				return
			}
			next(writer, request)
		})

		return fn
	}

	businessVerifyHandler1 := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		webhooks.SubscriptionVerificationHandlerFunc("business-subscription-token")(writer, request)
	})

	http.HandleFunc("POST /webhooks/messages", validatorWrapper(messageWebhooksHandler))
	http.HandleFunc("POST /webhooks/business", validatorWrapper(businessWebhooksHandler))
	http.HandleFunc("POST /webhooks/flows", flowEventsHandler)
	http.HandleFunc("POST /webhooks/messages/verify", messageVerifyToken.VerifySubscription)
	http.HandleFunc("POST /webhooks/business/verify", businessVerifyToken.VerifySubscription)
	http.HandleFunc("POST /webhooks/business/verify/2", businessVerifyHandler1)
	http.ListenAndServe(":8080", nil)
}

See more in examples

Testing

There is provision of mocks that may come handy in testing.

Links