Code Monkey home page Code Monkey logo

whatsapp-business-cloud-api-net's Introduction

WhatsApp Business Cloud API C# Wrapper Library for .NET Developers

A Wrapper for Whatsapp Business Cloud API hosted by Meta.

Build status NuGet version (WhatsappBusiness.CloudApi)

Official API Documentation: Meta for Developers

Sell Product and Services: Product Message Configuration

Account Metrics: Account Metrics Configuration for analytics and conversation analytics

QR Code Message Management: QR Code Messages for WhatsApp Business

WhatsApp Flows: Setting up WhatsApp Flows

Webhook Configuration Documentation: WhatsApp Cloud API Webhook

Authentication Message Documentation: Create and Send Authentication Message

WhatsApp Cloud API Error Codes: Error Codes

Take note: Sending a message to a phone number format 00[Country Code] xx xx xx using the prefix 00 before the country code will make the cloud API return an invalid parameter error (#100) (Credits @Tekkharibo)

Take note: Downloading media from the generated Whatsapp media URL will require one to specify the app name and version value to be set as useragent for the download media function to work. It is included as properties for the config class. (Credits @atmtrujillo)

Capabilities

Note: This package is WIP. The capabilities of Cloud API will be reflected soon. Feel free to contribute!

  • Sending Messages

    • Text
    • Media (image, video, audio, document, sticker)
    • Contact
    • Location
    • Interactive (List, Reply)
    • Template (text, image, video, document, authentication, flow message, carousel, catalog message, limited-time offer message, product message)
    • Template Messages with parameters (text, image, video, document, authentication, product message)
    • Single Product Message
    • Multiple Product Message
    • Authentication Message
    • Flow Message
  • Receiving Message (via Webhook)

    • Text
    • Media (image, video, audio, document, sticker)
    • Contact
    • Location
    • Interactive (List, Reply)
    • Button
  • WhatsApp Business Management API

    • QR Code Message Management
    • Account Metrics
    • Template Management
  • Sample project

    • Read csv, and send over WhatsApp per record
    • Implements many of the samples of below
    • Upload files to local server, before uploading to WhatsApp

Installation

  • PackageManager: PM> Install-Package WhatsappBusiness.CloudApi
  • DotNetCLI: > dotnet add package WhatsappBusiness.CloudApi

Setting yourself for successful WhatsApp Business Cloud Api integration

Before you proceed kindly acquaint yourself with WhatsApp Business Cloud Apis by going through the Docs in Meta's developer portal if you like.

  1. Obtain a Temporary access token for the meta developers portal.

  2. Ensure your project is running on the minimum supported versions of .Net

  3. WhatsAppBusinessCloudAPi is dependency injection (DI) friendly and can be readily injected into your classes. You can read more on DI in Asp.Net core here. If you can't use DI you can always manually create a new instance of WhatsAppBusinessClient and pass in an httpClient instance in it's constructor. eg.

// When Dependency Injection is not possible...

//create httpclient instance
var httpClient = new HttpClient();

httpClient.BaseAddress = WhatsAppBusinessRequestEndpoint.BaseAddress;
	
//create WhatsAppBusiness API client instance
var whatsAppBusinessClient = new WhatsAppBusinessClient(httpClient, whatsAppConfig); //make sure to pass httpclient and whatsAppConfig instance as an argument
	

I would recommend creating WhatsAppBusinessClient using Dependency Injection. [Optional] You can use any IOC container or Microsoft DI container in your legacy projects.

// Adding Dependency Injection into legacy projects

public static IServiceProvider ServiceProvider;


// To be used in the main application startup method
void Application_Start(object sender, EventArgs e)
{
  var hostBuilder = new HostBuilder();
  hostBuilder.ConfigureServices(ConfigureServices);
  var host = hostBuilder.Build();

  ServiceProvider = host.Services;
}

void ConfigureServices(IServiceCollection services)
{
   services.AddHttpClient<IWhatsAppBusinessClient, WhatsAppBusinessClient>(options => options.BaseAddress = WhatsAppBusinessRequestEndpoint.BaseAddress);
   //inject services here
}
	

Registering WhatsAppBusinessClient & Set the BaseAddress -Dependency Injection Method in ASPNETCORE

  • Install WhatsappBusiness.CloudApi Project via Nuget Package Manager Console or Nuget Package Manager GUI.

For ASPNETCORE projects

  • In Program.cs add the namespace...
using WhatsappBusiness.CloudApi.Configurations;
using WhatsappBusiness.CloudApi.Extensions;
  • Inside ConfigureServices method add the following
WhatsAppBusinessCloudApiConfig whatsAppConfig = new WhatsAppBusinessCloudApiConfig();
whatsAppConfig.WhatsAppBusinessPhoneNumberId = builder.Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["WhatsAppBusinessPhoneNumberId"];
whatsAppConfig.WhatsAppBusinessAccountId = builder.Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["WhatsAppBusinessAccountId"];
whatsAppConfig.WhatsAppBusinessId = builder.Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["WhatsAppBusinessId"];
whatsAppConfig.AccessToken = builder.Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["AccessToken"];
builder.Services.AddWhatsAppBusinessCloudApiService(whatsAppConfig);
  • Once the WhatsAppBusinessClient is registered, you can pass it and use it in your classes to make API calls to WhatsApp Business Cloud API Server as follows;
public class SendMessageController
{
	private readonly IWhatsAppBusinessClient _whatsAppBusinessClient;
	public SendMessageController(IWhatsAppBusinessClient whatsAppBusinessClient)
	{
		_whatsAppBusinessClient = whatsAppBusinessClient;
	}
	....
	//code omitted for brevity
}

Send Text Message Request

TextMessageRequest textMessageRequest = new TextMessageRequest();
textMessageRequest.To = "Recipient Phone Number";
textMessageRequest.Text = new WhatsAppText();
textMessageRequest.Text.Body = "Message Body";
textMessageRequest.Text.PreviewUrl = false;

var results = await _whatsAppBusinessClient.SendTextMessageAsync(textMessageRequest);

Send Audio Message Request

AudioMessageByUrlRequest audioMessage = new AudioMessageByUrlRequest();
audioMessage.To = "Recipient Phone Number";
audioMessage.Audio = new MediaAudioUrl();
audioMessage.Audio.Link = "Audio Url";

var results = await _whatsAppBusinessClient.SendAudioAttachmentMessageByUrlAsync(audioMessage);

Send Document Message Request

DocumentMessageByUrlRequest documentMessage = new DocumentMessageByUrlRequest();
documentMessage.To = "Recipient Phone Number";
documentMessage.Document = new MediaDocumentUrl();
documentMessage.Document.Link = "Document Url";

var results = await _whatsAppBusinessClient.SendDocumentAttachmentMessageByUrlAsync(documentMessage);

Send Image Message Request

ImageMessageByUrlRequest imageMessage = new ImageMessageByUrlRequest();
imageMessage.To = "Recipient Phone Number";
imageMessage.Image = new MediaImageUrl();
imageMessage.Image.Link = "Image Url";

var results = await _whatsAppBusinessClient.SendImageAttachmentMessageByUrlAsync(imageMessage);

Send Sticker Message Request

StickerMessageByUrlRequest stickerMessage = new StickerMessageByUrlRequest();
stickerMessage.To = "Recipient Phone Number";
stickerMessage.Sticker = new MediaStickerUrl();
stickerMessage.Sticker.Link = "Sticker Url";

var results = await _whatsAppBusinessClient.SendStickerMessageByUrlAsync(stickerMessage);

Send Video Message Request

VideoMessageByUrlRequest videoMessage = new VideoMessageByUrlRequest();
videoMessage.To = "Recipient Phone Number";
videoMessage.Video = new MediaVideoUrl();
videoMessage.Video.Link = "Video url";

var results = await _whatsAppBusinessClient.SendVideoAttachmentMessageByUrlAsync(videoMessage);

Send Contact Message Request

ContactMessageRequest contactMessageRequest = new ContactMessageRequest();
contactMessageRequest.To = "Recipient Phone Number";
contactMessageRequest.Contacts = new List<ContactData>()
{
    new ContactData()
    {
        Addresses = new List<Address>()
        {
            new Address()
            {
                State = "State Test",
                City = "City Test",
                Zip = "Zip Test",
                Country = "Country Test",
                CountryCode = "Country Code Test",
                Type = "Home"
            }
        },
        Name = new Name()
        {
            FormattedName = "Testing name",
            FirstName = "FName",
            LastName = "LName",
            MiddleName = "MName"
        }
    }
};

var results = await _whatsAppBusinessClient.SendContactAttachmentMessageAsync(contactMessageRequest);

Send Location Message Request

LocationMessageRequest locationMessageRequest = new LocationMessageRequest();
locationMessageRequest.To = "Recipient Phone Number";
locationMessageRequest.Location = new Location();
locationMessageRequest.Location.Name = "Location Test";
locationMessageRequest.Location.Address = "Address Test";
locationMessageRequest.Location.Longitude = "location longitude";
locationMessageRequest.Location.Latitude = "location latitude";

var results = await _whatsAppBusinessClient.SendLocationMessageAsync(locationMessageRequest);

Send Interactive List Message Request

InteractiveListMessageRequest interactiveListMessage = new InteractiveListMessageRequest();
interactiveListMessage.To = "Recipient Phone Number";
interactiveListMessage.Interactive = new InteractiveListMessage();

interactiveListMessage.Interactive.Header = new Header();
interactiveListMessage.Interactive.Header.Type = "text";
interactiveListMessage.Interactive.Header.Text = "List Header Sample Test";

interactiveListMessage.Interactive.Body = new ListBody();
interactiveListMessage.Interactive.Body.Text = "List Message Body";

interactiveListMessage.Interactive.Footer = new Footer();
interactiveListMessage.Interactive.Footer.Text = "List Footer Sample Test";

interactiveListMessage.Interactive.Action = new ListAction();
interactiveListMessage.Interactive.Action.Button = "Send";
interactiveListMessage.Interactive.Action.Sections = new List<Section>()
{
    new Section()
    {
        Title = "Category A",
        Rows = new List<Row>()
        {
            new Row()
            {
                Id = "Item_A1",
                Title = "Apples",
                Description = "Enjoy fruits for free"
            },
            new Row()
            {
                Id = "Item_A2",
                Title = "Tangerines",
                Description = "Enjoy fruits for free"
            },
        },
    },
    new Section()
    {
        Title = "Category B",
        Rows = new List<Row>()
        {
            new Row()
            {
                Id = "Item_B1",
                Title = "2JZ",
                Description = "Engine discounts"
            },
            new Row()
            {
                Id = "Item_2",
                Title = "1JZ",
                Description = "Engine discounts"
            },
        }
    }
};

var results = await _whatsAppBusinessClient.SendInteractiveListMessageAsync(interactiveListMessage);

Send Interactive Reply Button Request

InteractiveReplyButtonMessageRequest interactiveReplyButtonMessage = new InteractiveReplyButtonMessageRequest();
interactiveReplyButtonMessage.To = "Recipient Phone Number";
interactiveReplyButtonMessage.Interactive = new InteractiveReplyButtonMessage();

interactiveReplyButtonMessage.Interactive.Body = new ReplyButtonBody();
interactiveReplyButtonMessage.Interactive.Body.Text = "Reply Button Body";

interactiveReplyButtonMessage.Interactive.Action = new ReplyButtonAction();
interactiveReplyButtonMessage.Interactive.Action.Buttons = new List<ReplyButton>()
{
    new ReplyButton() 
    {
        Type = "reply",
        Reply = new Reply()
        {
            Id = "SAMPLE_1_CLICK",
            Title = "CLICK ME!!!"
        }
    },

    new ReplyButton()
    {
        Type = "reply",
        Reply = new Reply()
        {
            Id = "SAMPLE_2_CLICK",
            Title = "LATER"
        }
    }
};

var results = await _whatsAppBusinessClient.SendInteractiveReplyButtonMessageAsync(interactiveReplyButtonMessage);

Send Template Message Request

TextTemplateMessageRequest textTemplateMessage = new TextTemplateMessageRequest();
textTemplateMessage.To = "Recipient Phone Number";
textTemplateMessage.Template = new TextMessageTemplate();
textTemplateMessage.Template.Name = "Template Name";
textTemplateMessage.Template.Language = new TextMessageLanguage();
textTemplateMessage.Template.Language.Code = "en_US";

var results = await _whatsAppBusinessClient.SendTextMessageTemplateAsync(textTemplateMessage);

Send Text Template Message with parameters request

// For Text Template message with parameters supported component type is body only
TextTemplateMessageRequest textTemplateMessage = new TextTemplateMessageRequest();
textTemplateMessage.To = sendTemplateMessageViewModel.RecipientPhoneNumber;
textTemplateMessage.Template = new TextMessageTemplate();
textTemplateMessage.Template.Name = sendTemplateMessageViewModel.TemplateName;
textTemplateMessage.Template.Language = new TextMessageLanguage();
textTemplateMessage.Template.Language.Code = LanguageCode.English_US;
textTemplateMessage.Template.Components = new List<TextMessageComponent>();
textTemplateMessage.Template.Components.Add(new TextMessageComponent()
{
    Type = "body",
    Parameters = new List<TextMessageParameter>()
    {
	new TextMessageParameter()
	{
	    Type = "text",
	    Text = "Testing Parameter Placeholder Position 1"
	},
	new TextMessageParameter()
	{
	    Type = "text",
	    Text = "Testing Parameter Placeholder Position 2"
	}
    }
});

var results = await _whatsAppBusinessClient.SendTextMessageTemplateAsync(textTemplateMessage);

Send Media Template Message with parameters

// Tested with facebook predefined template name: sample_movie_ticket_confirmation
ImageTemplateMessageRequest imageTemplateMessage = new ImageTemplateMessageRequest();
imageTemplateMessage.To = sendTemplateMessageViewModel.RecipientPhoneNumber;
imageTemplateMessage.Template = new ImageMessageTemplate();
imageTemplateMessage.Template.Name = sendTemplateMessageViewModel.TemplateName;
imageTemplateMessage.Template.Language = new ImageMessageLanguage();
imageTemplateMessage.Template.Language.Code = LanguageCode.English_US;
imageTemplateMessage.Template.Components = new List<ImageMessageComponent>()
{
    new ImageMessageComponent()
    {
	Type = "header",
	Parameters = new List<ImageMessageParameter>()
	{
	    new ImageMessageParameter()
	    {
		Type = "image",
		Image = new Image()
		{
		    Link = "https://otakukart.com/wp-content/uploads/2022/03/Upcoming-Marvel-Movies-In-2022-23.jpg"
		}
	    }
	},
    },
    new ImageMessageComponent()
    {
	Type = "body",
	Parameters = new List<ImageMessageParameter>()
	{
	    new ImageMessageParameter()
	    {
		Type = "text",
		Text = "Movie Testing"
	    },

	    new ImageMessageParameter()
	    {
		Type = "date_time",
		DateTime = new ImageTemplateDateTime()
		{
		    FallbackValue = DateTime.Now.ToString("dddd d, yyyy"),
		    DayOfWeek = (int)DateTime.Now.DayOfWeek,
		    Year = DateTime.Now.Year,
		    Month = DateTime.Now.Month,
		    DayOfMonth = DateTime.Now.Day,
		    Hour = DateTime.Now.Hour,
		    Minute = DateTime.Now.Minute,
		    Calendar = "GREGORIAN"
		}
	    },

	    new ImageMessageParameter()
	    {
		Type = "text",
		Text = "Venue Test"
	    },

	    new ImageMessageParameter()
	    {
		Type = "text",
		Text = "Seat 1A, 2A, 3A and 4A"
	    }
	}
    }
};

var results = await _whatsAppBusinessClient.SendImageAttachmentTemplateMessageAsync(imageTemplateMessage);

Send Interactive Template Message with parameters

// Tested with facebook predefined template name: sample_issue_resolution
InteractiveTemplateMessageRequest interactiveTemplateMessage = new InteractiveTemplateMessageRequest();
interactiveTemplateMessage.To = sendTemplateMessageViewModel.RecipientPhoneNumber;
interactiveTemplateMessage.Template = new InteractiveMessageTemplate();
interactiveTemplateMessage.Template.Name = sendTemplateMessageViewModel.TemplateName;
interactiveTemplateMessage.Template.Language = new InteractiveMessageLanguage();
interactiveTemplateMessage.Template.Language.Code = LanguageCode.English_US;
interactiveTemplateMessage.Template.Components = new List<InteractiveMessageComponent>();
interactiveTemplateMessage.Template.Components.Add(new InteractiveMessageComponent()
{
    Type = "body",
    Parameters = new List<InteractiveMessageParameter>()
    {
	new InteractiveMessageParameter()
	{
	    Type = "text",
	    Text = "Interactive Parameter Placeholder Position 1"
	}
    }
});

var results = await _whatsAppBusinessClient.SendInteractiveTemplateMessageAsync(interactiveTemplateMessage);

Sending Authentication Message

// You need to create your authentication template message
AuthenticationTemplateMessageRequest authenticationTemplateMessageRequest = new();
authenticationTemplateMessageRequest.To = sendTemplateMessageViewModel.RecipientPhoneNumber;
authenticationTemplateMessageRequest.Template = new();
authenticationTemplateMessageRequest.Template.Name = sendTemplateMessageViewModel.TemplateName;
authenticationTemplateMessageRequest.Template.Language = new();
authenticationTemplateMessageRequest.Template.Language.Code = LanguageCode.English_US;
authenticationTemplateMessageRequest.Template.Components = new List<AuthenticationMessageComponent>()
{
    new AuthenticationMessageComponent()
    {
	Type = "body",
	Parameters = new List<AuthenticationMessageParameter>()
	{
	    new AuthenticationMessageParameter()
	    {
		Type = "text",
		Text = "J$FpnYnP" // One time password value
	    }
	}
    },
    new AuthenticationMessageComponent()
    {
	Type = "button",
	SubType = "url",
	Index = 0,
	Parameters = new List<AuthenticationMessageParameter>()
	{
	    new AuthenticationMessageParameter()
	    {
		Type = "text",
		Text = "J$FpnYnP" // One time password value
	    }
	}
    }
};

var results = await _whatsAppBusinessClient.SendAuthenticationMessageTemplateAsync(authenticationTemplateMessageRequest);

Sending Flow Message

FlowMessageRequest flowMessageRequest = new FlowMessageRequest();
flowMessageRequest.To = sendFlowMessageViewModel.RecipientPhoneNumber;
flowMessageRequest.Interactive = new FlowMessageInteractive();

flowMessageRequest.Interactive.Header = new FlowMessageHeader();
flowMessageRequest.Interactive.Header.Type = "text";
flowMessageRequest.Interactive.Header.Text = "Header flow";

flowMessageRequest.Interactive.Body = new FlowMessageBody();
flowMessageRequest.Interactive.Body.Text = "Body flow";

flowMessageRequest.Interactive.Footer = new FlowMessageFooter();
flowMessageRequest.Interactive.Footer.Text = "Footer flow";

flowMessageRequest.Interactive.Action = new FlowMessageAction();
flowMessageRequest.Interactive.Action.Parameters = new FlowMessageParameters();
flowMessageRequest.Interactive.Action.Parameters.FlowToken = sendFlowMessageViewModel.FlowToken;
flowMessageRequest.Interactive.Action.Parameters.FlowId = sendFlowMessageViewModel.FlowId;
flowMessageRequest.Interactive.Action.Parameters.FlowCta = sendFlowMessageViewModel.FlowButtonText;
flowMessageRequest.Interactive.Action.Parameters.FlowAction = sendFlowMessageViewModel.SelectedFlowAction;
flowMessageRequest.Interactive.Action.Parameters.IsInDraftMode = (sendFlowMessageViewModel.SelectedMode.Equals("Draft", StringComparison.OrdinalIgnoreCase));

flowMessageRequest.Interactive.Action.Parameters.FlowActionPayload = new FlowActionPayload();
flowMessageRequest.Interactive.Action.Parameters.FlowActionPayload.Screen = sendFlowMessageViewModel.ScreenId;

var results = await _whatsAppBusinessClient.SendFlowMessageAsync(flowMessageRequest);

Sending Flow Template Message

FlowTemplateMessageRequest flowTemplateMessageRequest = new FlowTemplateMessageRequest();
flowTemplateMessageRequest.To = sendTemplateMessageViewModel.RecipientPhoneNumber;
flowTemplateMessageRequest.Template = new();
flowTemplateMessageRequest.Template.Name = sendTemplateMessageViewModel.TemplateName;
flowTemplateMessageRequest.Template.Language = new();
flowTemplateMessageRequest.Template.Language.Code = LanguageCode.English_US;
flowTemplateMessageRequest.Template.Components = new List<FlowMessageComponent>()
{
    new FlowMessageComponent()
    {
        Type = "button",
        SubType = "flow",
        Index = 0,
        Parameters = new List<FlowTemplateMessageParameter>()
        {
            new FlowTemplateMessageParameter()
            {
                Type = "action",
                Action = new FlowTemplateMessageAction()
                {
                    FlowToken = "",
                }
            }
        }
    }
};

var results = await _whatsAppBusinessClient.SendFlowMessageTemplateAsync(flowTemplateMessageRequest);

Webhook Subscription

First, you need to setup the callback url and verify the token string for WhatsApp Cloud API to verify your callback url. Verification part

[HttpGet("<YOUR ENDPOINT ROUTE>")]
public ActionResult<string> ConfigureWhatsAppMessageWebhook([FromQuery(Name = "hub.mode")] string hubMode,
                                                                    [FromQuery(Name = "hub.challenge")] int hubChallenge,
                                                                    [FromQuery(Name = "hub.verify_token")] string hubVerifyToken)
{
return Ok(hubChallenge);
}

Receiving Messages

[HttpPost("<YOUR ENDPOINT ROUTE>")]
public IActionResult ReceiveWhatsAppTextMessage([FromBody] dynamic messageReceived)
{
// Logic to handle different type of messages received
return Ok();
}

Reply to Message

TextMessageReplyRequest textMessageReplyRequest = new TextMessageReplyRequest();
textMessageReplyRequest.Context = new TextMessageContext();
textMessageReplyRequest.Context.MessageId = textMessage.SingleOrDefault().Id;
textMessageReplyRequest.To = textMessage.SingleOrDefault().From;
textMessageReplyRequest.Text = new WhatsAppText();
textMessageReplyRequest.Text.Body = "Your Message was received. Processing the request shortly";
textMessageReplyRequest.Text.PreviewUrl = false;

await _whatsAppBusinessClient.SendTextMessageAsync(textMessageReplyRequest);

Verify Webhook X-Hub-Signature-256 (Credits @Tekkharibo)

[HttpPost]
public async Task<IActionResult> GetMessage()
{
    string stringifiedBody;

    string xHubSignature256 = this.HttpContext.Request.Headers["X-Hub-Signature-256"].ToString();

    using (var sr = new StreamReader(this.HttpContext.Request.Body))
    {
        stringifiedBody = await sr.ReadToEndAsync().ConfigureAwait(false);
    }

    string xHubSignature256Result = FacebookWebhookHelper.CalculateSignature(this._configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["AppSecret"], stringifiedBody);

    if (!String.Equals(xHubSignature256, xHubSignature256Result, StringComparison.InvariantCultureIgnoreCase))
        return this.Unauthorized("Invalid Signature");

    return this.Ok();
}

Verify Webhook X-Hub-Signature-256 (.NET 7 Minimal API) (Credits @sapharos)

app.Use((context, next) =>
{
	context.Request.EnableBuffering();
	return next();
});

// Validation implementation

string stringifiedBody;
string xHubSignature256 = context.Request.Headers["X-Hub-Signature-256"].ToString();
context.Request.Body.Seek(0, SeekOrigin.Begin);
using (var sr = new StreamReader(context.Request.Body))
{
	stringifiedBody = await sr.ReadToEndAsync().ConfigureAwait(false);
}
string xHubSignature256Result = FacebookWebhookHelper.CalculateSignature(_config.GetValue<string("Facebook:AppSecret"), stringifiedBody);
if (!String.Equals(xHubSignature256, xHubSignature256Result,StringComparison.InvariantCultureIgnoreCase))
{
	return Results.Unauthorized();
}

Error handling

WhatsAppBusinessClient Throws WhatsappBusinessCloudAPIException It is your role as the developer to catch the exception and continue processing in your application. The snippet below shows how you can catch the WhatsappBusinessCloudAPIException.

using WhatsappBusiness.CloudApi.Exceptions; // add this to your class or namespace

try
{	
	return await _whatsAppBusinessClient.SendTextMessageAsync(textMessageRequest);
}
catch (WhatsappBusinessCloudAPIException ex)
{
	_logger.LogError(ex, ex.Message);
}		

Issues

If you will face any issues with the usage of this package please raise one so that we can quickly fix it as soon as possible.

Contributing

This is an open-source project under MIT License so anyone is welcome to contribute from typos, to source code to documentation.

Credits

  1. Gabriel
  2. All other contributors

whatsapp-business-cloud-api-net's People

Contributors

claudineibr avatar emisand avatar gabrieldwight avatar thecjman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

whatsapp-business-cloud-api-net's Issues

How can I read or receive messages

@gabrieldwight
First of all, thanks for the library, it is of great support for any research project.

I have a problem that I can't solve.
How can I read or receive the messages that a contact writes to me, I have already tried everything, I have configured the WebHooks from Meta, through the Glitch I can read the messages, but not through the methods of this project.
Could you give me a hand?

SendWhatsAppTextMessage does not send message in test project

Discussed in #17

Originally posted by Catronis October 13, 2022
Hello, I downloaded the project a week ago and did some testing, a simple message sent correctly. I downloaded the most current version again today but it doesn't send a message, template sends normally. However, it notifies that the message has been sent, but does not reach whatsapp.

Receiving and processing audio type messages

Hi,

Thank you so much for all your work in facilitating the use of the WhatsApp Business Cloud APIs.

I've been trying to create a WhatsApp bot to transcribe voice messages, and I'm struggling to download the audio.

I've downloaded and been able to reproduce voice messages through Postman, but I can't make it work in C#. I looked at your project and couldn't find the implementation to receive and process an audio message.

Is this something you are planning to implement, or did I miss it?

Thanks again

Regards,
Alex

No Error but no Message received and no Id is found?

Hello im using the project in a C# Console Test (.NET 7.0), i have configurated the app in the developer tool and Access Token but when i send a message to my number the message isn't receied and also the BusinessId is not found from the NumberId ?
Can you help me to find the error? There is not Exception.

image

Send bulk messages

I was trying to send bulk messages by adding comma separated phone numbers in RecipientPhoneNumber, but getting invalid parameter error.

TextMessageRequest textMessageRequest = new();
textMessageRequest.To = "1122334455,1122334456";

Is there an alternative to send bulk messages in single api?

Stiker method its not working

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

"Reply to Message" feature is not marking messages as replies in certain scenarios

When replying to a message, the message is sent but not marked as replying (with some exceptions)
The feature "reply to message" doesn't work when the system is replying to a message the user sent, but is working when:

  1. Replying to a message the user sent replying to other message
  2. Replying to a message the system sent

issue_whatsapp_reply

To Reproduce

        var textMessageReplyRequest = new TextMessageReplyRequest
        {
            Context = new TextMessageContext { MessageId = request.QuotedMessageId },
            To = request.Number,
            Text = new WhatsAppText
            {
                Body = request.Text,
                PreviewUrl = false
            }
        };

        var results = await _whatsAppBusinessClient.SendTextMessageAsync(textMessageReplyRequest, null, cancellationToken);

Unable to edit Whatsapp Business Account subscription

First of all thank you for this lib, it has been the most useful overall.

As of now I have not been able to setup the callback URL for the webhook, while testing the connection while the project runs on HTTPS localhost, i get the next error message:

The URL couldn't be validated. Callback verification failed with the following errors: curl_errno = 56; curl_error = Received HTTP code 403 from proxy after CONNECT; HTTP Status Code = 403; HTTP Message = Forbidden
url

I have already checked time and time again my VerifyToken, WhatsAppBusinessPhoneNumberId, WhatsAppBusinessAccountId, WhatsAppBusinessId, AccessToken.

As of now i'm using .net core 6.0

Any kind of help will be of great assistance and thank you in advance for your time.

Error when sending messages

{"An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set."}

Startup.CS
WhatsAppBusinessCloudApiConfig whatsAppConfig = new WhatsAppBusinessCloudApiConfig(); whatsAppConfig.WhatsAppBusinessPhoneNumberId = Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["WhatsAppBusinessPhoneNumberId"]; whatsAppConfig.WhatsAppBusinessAccountId = Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["WhatsAppBusinessAccountId"]; whatsAppConfig.WhatsAppBusinessId = Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["WhatsAppBusinessId"]; whatsAppConfig.AccessToken = Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["AccessToken"]; services.AddWhatsAppBusinessCloudApiService(whatsAppConfig);

Controller

try
   {
       TextMessageRequest textMessageRequest = new TextMessageRequest();
       textMessageRequest.To = textMessageLast.From;
       textMessageRequest.Text = new WhatsAppText();
       textMessageRequest.Text.Body = "Test";
       textMessageRequest.Text.PreviewUrl = false;

       var results = await _whatsAppBusinessClient.SendTextMessageAsync(textMessageRequest);

       return Ok();
   }
   catch (WhatsappBusinessCloudAPIException ex)
   {
       return Ok(ex.Message);
   }


Why is the Footer-Property is missing?

Hello!

I would like to send a custom footer with various message types.
But none of these (except the InteractiveListMessageRequest) have a footer property (e.g. InteractiveReplyButtonMessageRequest). How should I do this (I only saw the class WhatsappBusiness.CloudApi.Messages.Requests.Footer but this is implemented only on the InteractiveListMessageRequest) or do you plan to implement this?

Best regards

CTA buttons for Interactive messages

Is your feature request related to a problem? Please describe.
Interactive Messages can also send a Click to Action Button with a url parameter

Describe the solution you'd like
see here:
https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages/

"action": {
"name": "cta_url",
"parameters": {
"display_text": "See Dates",
"url": "https://www.luckyshrub.com?clickID=kqDGWd24Q5TRwoEQTICY7W1JKoXvaZOXWAS7h1P76s0R7Paec4"
}
}

Thanks for the great library and quick support!!

Add X-Hub-Signature-256 control

Hi

I have add on my project the check of the X-Hub-Signature-256 and I share it to you if you want to add it on the library

My Solution
Get on controller the X-Hub-Signature-256

[HttpPost]
public async Task<IActionResult> GetMessage()
{
    string stringifiedBody;

    string xHubSignature256 = this.HttpContext.Request.Headers["X-Hub-Signature-256"].ToString();

    using (var sr = new StreamReader(this.HttpContext.Request.Body))
    {
        stringifiedBody = await sr.ReadToEndAsync().ConfigureAwait(false);
    }

    string xHubSignature256Result = FacebookWebhookHelper.CalculateSignature(this._configuration.GetSection("WhatsAppBusinessCloudApiConfiguration")["AppSecret"], stringifiedBody);

    if (!String.Equals(xHubSignature256, xHubSignature256Result, StringComparison.InvariantCultureIgnoreCase))
        return this.Unauthorized("Invalid Signature");

    return this.Ok();
}

Process to check the X-Hub-Signature-256

using System.Security.Cryptography;
using System.Text;

namespace MyCustomNameSpace.Helpers;

public static class FacebookWebhookHelper
{
    /// <summary>
    /// The HTTP request will contain an X-Hub-Signature header which contains the SHA1 signature of the request payload,
    /// using the app secret as the key, and prefixed with sha1=.
    /// Your callback endpoint can verify this signature to validate the integrity and origin of the payload
    /// </summary>
    /// <param name="appSecret">facebook app secret</param>
    /// <param name="payload">body of webhook post request</param>
    /// <returns>calculated signature</returns>
    public static string CalculateSignature(string appSecret, string payload)
    {
        /*
         Please note that the calculation is made on the escaped unicode version of the payload, with lower case hex digits.
         If you just calculate against the decoded bytes, you will end up with a different signature.
         For example, the string äöå should be escaped to \u00e4\u00f6\u00e5.
         */

        using HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(appSecret));
        hmac.Initialize();
        byte[] hashArray = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
        string hash = $"SHA256={BitConverter.ToString(hashArray).Replace("-", string.Empty)}";

        return hash;
    }
}

Other observation
If I try to get the object with something other that new StreamReader(this.HttpContext.Request.Body), the X-Hub-Signature-256 control fail any time

Lost last name when send contact message request

I tried to send a contact message request with the follows data,
I received all the data on WhatsApp, then I saved the contact to memory and I lost the last name.

Does the last name feature no longer exist or has there been a parsing error in your program?

    ContactMessageModel contactMessageModel = new ContactMessageModel();
    contactMessageModel.ReceiptPhoneNumber = phoneNumber;
    contactMessageModel.ContactDataList = new List < ContactData > () {
        new ContactData() {
            Addresses = new List < Address > () {
                    new Address() {
                        City = "city1",
                            Country = "country1",
                            CountryCode = "countrycode1",
                            State = "state1",
                            Street = "street1",
                            Zip = "zip1",
                            Type = "Work"
                    }
                },
                Emails = new List < Email > () {
                    new Email() {
                        EmailEmail = "[email protected]",
                            Type = "Work"
                    }
                },
                Name = new Name() {
                    FormattedName = "fullname1",
                        FirstName = "firstname1",
                        LastName = "lastname1" // MISSING IN HERE
                },
                Org = new WhatsappBusiness.CloudApi.Messages.Requests.Org() {
                    Company = "company1",
                        Department = "dev1",
                        Title = "title1"
                },
                Phones = new List < Phone > () {
                    new Phone() {
                        PhonePhone = "0810000000000",
                            Type = "Work"
                    }
                },
                Urls = new List < Url > () {
                    new Url() {
                        UrlUrl = "www.xxxx..id",
                            Type = "Work"
                    }
                }
        }
    };
    _whatsAppCloudAPI.SendContactMessageRequest(contactMessageModel);

Thanks,
Yudha

Line break is not applying in whatsapp messages

Describe the bug
I want to send a message with line break. As per whatsapp API guide line we can send \n in message for line break. I have tried this in postman directly there it is working but not working with this nuget.

To Reproduce
Steps to reproduce the behavior:

I am calling send message like

TextMessageReplyRequest request = new();
request.To = "phone numbe";
request.Text = new WhatsAppText();
request.Text.Body = "Hello,\nWelcome here";
request.Text.PreviewUrl = false;
_whatsAppBusinessClient.SendTextMessageAsync(request);

Expected behavior
This message should be delivered in two lines. but it delivered like Hello,\nWelcome here

Problem
I think this is serialization problem. I have checked in fiddler, in json this message is serialized as Hello,\nWelcome here

New Authentication Messages not supported

The WhatsApp Cloud API for sending authentication messages has changed and I think authentication messages are not supported with current version anymore. Here is the link to the docs how a authentication message looks like now:

WhatsApp Cloud Docu

{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "<CUSTOMER_PHONE_NUMBER>",
  "type": "template",
  "template": {
    "name": "<TEMPLATE_NAME>",
    "language": {
      "code": "<TEMPLATE_LANGUAGE_CODE>"
    },
    "components": [
      {
        "type": "body",
        "parameters": [
          {
            "type": "text",
            "text": "<ONE-TIME PASSWORD>"
          }
        ]
      },
      {
        "type": "button",
        "sub_type": "url",
        "index": "0",
        "parameters": [
          {
            "type": "text",
            "text": "<ONE-TIME PASSWORD>"
          }
        ]
      }
    ]
  }
}

The TextMessageComponent currently only takes TextMessageParameters, but to send a one-time password other options should be addable to the List of TextMessageComponents

Error on CreateTemplateMessageAsync

Describe the bug
When calling the CreateTemplateMessageAsync method, I receive an error WhatsappBusinessCloudAPIException: (#100) The parameter name is required.

Upon checking the code and implementation, I noticed that there is no body being sent in the request.

My call:

 var templateData = new
 {
     name = "teste_nome_template_01",
     category = "MARKETING",
     allow_category_change = false,
     language = "en_US",
     components = new List<object>()
     {
         new
         {
             type = "BODY",
             text = "Test",
             parameters = new[]
             {
                 new { type = "text", text = "test 01" }
             }
         },
         new
         {
             type = "BUTTONS",
             buttons= new List<object>()
             {
               new {
                 type = "PHONE_NUMBER",
                 text = "Call",
                 phone_number= "********************"
               }
             }
         },
         new
         {
             type = "FOOTER",
             text = "footer template"
         }
     }
 };

 var results = await _whatsAppBusinessClient.CreateTemplateMessageAsync("********************", templateData);

I believe the issue might be due to calling the WhatsAppBusinessPostAsync method without passing the template parameter, which due to overload, ends up invoking the wrong method.

 public async Task<TemplateMessageCreationResponse> CreateTemplateMessageAsync(string whatsAppBusinessAccountId, object template, CancellationToken cancellationToken = default(CancellationToken))
 {
     string whatsAppBusinessEndpoint = WhatsAppBusinessRequestEndpoint.CreateTemplateMessage.Replace("{{WABA-ID}}", whatsAppBusinessAccountId);
     return await WhatsAppBusinessPostAsync<TemplateMessageCreationResponse>(whatsAppBusinessEndpoint, cancellationToken);
 }

Error when download media from url

var results = _whatsAppBusinessClient.GetMediaUrl(id);
var imageBytes = _whatsAppBusinessClient.DownloadMedia(results.Url); //I have an error on this line

Exception captured
System.ArgumentException: 'The value cannot be null or empty. Arg_ParamName_Name'
exception

MarkMessageAsReadAsync should allow passing the WhatsAppBusinessCloudApiConfig information similar to SendTextMessageAsync

Is your feature request related to a problem? Please describe.
we have multiple phone numbers associated with our app, so our message payload can contain different WhatsAppBusinessPhoneNumberId, we want to send the "MessageRead" response based on the WhatsAppBusinessPhoneNumberId we got from the payload, the "SendTextMessageAsync" method supports an overload where we can override the "WhatsAppBusinessPhoneNumberId" in the WhatsAppBusinessCloudApiConfig parameter.

Describe the solution you'd like
MarkMessageAsReadAsync should an overload which can take in WhatsAppBusinessCloudApiConfig parameter

Thanks -Nen

Add an option to override WhatsAppBusinessCloudApiConfig for specific messages

Currently when using this library with dependency injection we can only use the WABA and Phone Number provided when configuring the service provider.

Our use case requires using different phone numbers for each country we operate with, and it seems that creating a new WhatsAppBusinessClient for each message we send could be inefficient as a service provider is built and a new HttpClient is created for each new instance.

I suggest adding an optional parameter or using a context override to specify a different WhatsAppBusinessCloudApiConfig for each message we send, without the need of creating new WhatsAppBusinessClient instances.

[Information]Whatsapp error (#100) Invalid parameter on phone number

Hi,

Since the 2023 January 23 I can't send any message with the lib because I have the error (#100) Invalid parameter

I have found why I have this error.

The international phone number can write with two typing (French number for example)

  • +33 6 xx xx xx xx
  • 0033 6 xx xx xx xx

But the Whatsapp API, since this date don't allow the format 0033 6 xx xx xx xx

I think that was interested to add this information on the README.md

Regards

How to send text messages to groups

Hello, thanks for your contribution, it has been very useful to me, although now I also need to send messages to whatsapp groups, you know how I could achieve it, the truth is that I have looked for documentation but I have not been able to find it

how to send text messages

Hello, im trying to send a text message and it throws me this exception: WhatsappBusiness.CloudApi.Exceptions.WhatsappBusinessCloudAPIException: 'Unsupported post request. Object with ID 'messages' does not exist, cannot be loaded due to missing permissions, or does not support this operation.
i saw that builder.Configuration.GetSection("WhatsAppBusinessCloudApiConfiguration") is not working.
i would appreciate if you can provide an example connecting whatsapp business configurations such as phonenumberid with the project

Can we use this package for our VB.net Desktop application?

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

FIX: Error MarkMessageAsRead wrong API Method

Hi,

Thank you so much for this lib

I have an error with the function MarkMessageAsRead because that send request on PUT method but the Whatsapp API need a POST method

Error message:

Unsupported put request. Object with ID 'xxx' does not exist, cannot be loaded due to missing permissions, or does not support this operation. Please read the Graph API documentation at https://developers.facebook.com/docs/graph-api
Whatsapp documentation MarkMessageAsRead

Version

v1.0.2

Solve Issue

Need to update code on file WhatsAppBusinessClient.cs at line 205 and 211 update WhatsAppBusinessPutAsync by WhatsAppBusinessPostAsync

PS: I have never do a pull request, I will be try tomorrow for this fix

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.