Braintree Webhook - Check URL not working - c#

I have set up a webhook in my Braintree account https://example.com/Webhook/Accept
I have the site setup to create a Customer & Subscription via a purchase link.
When I make a purchase via the link these are both created ok in the Braintree vault, the webhook fires and I receive 2 notifications.
This all seems great, all is working, except the for 'Check URL' in the Braintree Control panel Webhook section, where im getting a HTTP error 500 internal server error.
I think its a bt_challenge issue on the GET but having tried a few variations I simply cant get it working.
Here is my controller:
public class WebhookController : Controller
{
public IBraintreeConfiguration config = new BraintreeConfiguration();
public ActionResult Accept()
{
CrmDB db = new CrmDB();
Log log;
var gateway = config.GetGateway();
if (Request.HttpMethod == "POST")
{
WebhookNotification webhookNotification = gateway.WebhookNotification.Parse(
Request.Params["bt_signature"],
Request.Params["bt_payload"]
);
string message = string.Format(
"[Webhook Received {0}] | Kind: {1} | Subscription: {2}",
webhookNotification.Timestamp.Value,
webhookNotification.Kind,
webhookNotification.Subscription.Id
);
System.Console.WriteLine(message);
// Save to Db
log = new Log
{
Stamp = DateTime.Now,
App = "api/Webhook/Accept",
IsInsight = true,
Insight = message
};
// Db
db.Log.Add(log);
db.SaveChanges();
return new HttpStatusCodeResult(200);
}
else
{
string msg = gateway.WebhookNotification.Verify(Request.QueryString["bt_challenge"]);
// Save to Db
log = new Log
{
Stamp = DateTime.Now,
App = "Webhook - bt_challenge",
IsInsight = true,
Insight = msg
};
// Db
db.Log.Add(log);
db.SaveChanges();
return Content(msg);
}
}
}
I believe the issue is in the else section (ie HTTP GET) but I cant work out what it is.
Why am I getting the 500 internal error from Webhook control Panel?

Related

Asp.Net - Stripe payment intent working locally but error 502 online

I'm trying to build a custom payment flow using Stripe. It's working locally, but when I try to put it online, I get an error after a certain time. I asked my web hosting service if they put any filter, but apparently not. You'll find the code bellow. For the Javascript, I basically copy pasted the code given by Stripe. Also, I don't get any payment intent on the Stripe end, so I think my web app can't connect to Stripe. Visually the form where the user is supposed to enter his card number etc isn't appearing.
The error I get after a certain time on the payment intent page
The server side code (C#):
[Authorize]
[Route("/create-payment-intent")]
[ApiController]
public class PaymentIntentApiController : Controller
{
private readonly UserManager<AppUser> userManager;
private readonly ApplicationDbContext db;
public PaymentIntentApiController(UserManager<AppUser> userManager, ApplicationDbContext db)
{
this.userManager = userManager;
this.db = db;
}
[HttpPost]
public async Task<ActionResult> Create(ProductViewModel request)
{
if (request == null)
{
return RedirectToAction("Checkout", "Payment");
}
ComplexDbOperations cdo = new ComplexDbOperations(db);
Data.Objects.Product product = new Data.Objects.Product { period = request.period, sub = request.subscription };
int price = ProductViewModel.calculateStripePrice(await cdo.getPricesAsync(product.ToRole().ToString()));
if (price <= 0)
{
return RedirectToAction("Checkout", "Payment");
}
AppUser user = await userManager.GetUserAsync(User);
if (user == null) return RedirectToAction("Index", "Home");
var paymentIntents = new PaymentIntentService();
var paymentIntentOptions = new PaymentIntentCreateOptions
{
Amount = price,
Currency = "chf",
Description = string.Format("Abonnement {0} pour {1}, Periode : {2}", request.subscription.ToString(), user.Email, request.period.ToString()),
Metadata = new Dictionary<string, string>
{
{"Abonnement", request.subscription.ToString()},
{"Periode", request.period.ToString() },
{"UserId", user.Id },
{"UserEmail", user.Email },
{"subCode", ((int)request.subscription).ToString()},
{"periodCode", ((int)request.period).ToString() },
}
};
var paymentIntent = paymentIntents.Create(paymentIntentOptions);
return Json(new { clientSecret = paymentIntent.ClientSecret });
}
}
What I've modified from Stripe on the client side code (js) :
// The items the customer wants to buy
var purchase = {
period: parseInt(document.getElementById("period").value),
subscription: parseInt(document.getElementById("subscription").value)
};
// Disable the button until we have Stripe set up on the page
document.querySelector("button").disabled = true;
fetch("/create-payment-intent", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(purchase)
})... (stripe code continuing)
Can you please help me ? I don't even know how I can get more details on the error as I can't debug the program when it's online.
Thanks in advance.
[Update]
I logged the server error and got :
System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. (api.stripe.com:443)
I finally know what was the problem so I'll explain what it was and how to solve it if someone encounters the same error.
The first thing I did was, as suggested by karllekko, to check the server logs when it was running on the hosted service. For that, I used Serilog which is a package that allows you to store the logs into a file (It was the simpliest solution for me).
I then got a more precise error :
HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time...
From this error, I knew I wasn't able to get to stripe's servers. I found a simple ping function in C# that checks if my assumption was right and it was.
I then checked with my host service provider how I could access an external IP: they use a proxy server for security reasons.
I added this code before creating the payment intent which uses the proxy server :
var handler = new HttpClientHandler
{
Proxy = new WebProxy(proxyServer),
UseProxy = true,
};
var httpClient = new HttpClient(handler);
var stripeClient = new StripeClient(
apiKey: secretApiKey,
httpClient: new SystemNetHttpClient(httpClient)
);
StripeConfiguration.StripeClient = stripeClient;
I hope if someone encounters the same error he will be able to solve the problem with this answer.

Why isn't Azure SignalR sending the messages published by the Azure Functions?

I have deployed a set of Azure Functions (v2).
I want to have an iOS application being notified when something happens on the server side so I created an Azure SignalR Service, configured as Serverless.
I have created and deployed a new negotiate Azure Function, as explained in the documentation, that publishes subscription information on a service bus queue:
[FunctionName("negotiate")]
public async Task<SignalRConnectionInfo> Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous)]
HttpRequest httpRequest,
ClaimsPrincipal claimsPrincipal,
[SignalRConnectionInfo(HubName = "updates", UserId = "{headers.x-ms-client-principal-id}")]
SignalRConnectionInfo connectionInfo)
{
try
{
// Uses the name identifier for now.
Claim nameIdentifierClaim = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
string userId = nameIdentifierClaim.Value;
foreach (Guid groupId in claimsPrincipal.GetGroups())
{
var subscription = new SynchronizationSubscriptionContract {UserId = userId, GroupId = groupId};
await m_serviceBus.SendAsync(JsonConvert.SerializeObject(subscription));
}
return connectionInfo;
}
catch (Exception exc)
{
//
}
}
The Service Bus is bound to an Azure Function that adds the user to the list of groups:
[ServiceBusAccount(Constants.Configuration.ServiceBusConnectionString)]
[FunctionName("subscribe")]
public async Task AddToGroupAsync(
[ServiceBusTrigger("synchronization-subscriptions")] string requestMessage,
[SignalR(HubName = "updates")] IAsyncCollector<SignalRGroupAction> signalRGroupActions)
{
var synchronizationSubscriptionContract = JsonConvert.DeserializeObject<SynchronizationSubscriptionContract>(requestMessage);
string groupName = synchronizationSubscriptionContract.GroupId;
var addGroupAction = new SignalRGroupAction
{
UserId = synchronizationSubscriptionContract.UserId,
GroupName = groupName,
Action = GroupAction.Add
};
await signalRGroupActions.AddAsync(addGroupAction);
}
So far so good.
Whenever one of my existing Azure Functions needs to publish a message on the SignalR channel, it sends a message on a dedicated service bus queue that is bound to another Azure Function. The Azure Function then grabs the message and sends it to the Azure SignalR Service:
[ServiceBusAccount(Constants.Configuration.ServiceBusConnectionString)]
[FunctionName(Api.Functions.Synchronization.UpdateSynchronizationInfo)]
public async Task Run(
[ServiceBusTrigger("synchronization-requests")] string requestMessage,
[SignalR(HubName = "updates")]
IAsyncCollector<SignalRMessage> signalRMessages
)
{
var requestContract = JsonConvert.DeserializeObject<SynchronizationUpdateRequestContract>(requestMessage);
var request = m_mapper.Map<SynchronizationUpdateRequest>(requestContract);
// Do more stuff.
string groupName = request.GroupId;
var updateMessage = new SignalRMessage
{
GroupName = groupName,
Target = "notify",
Arguments = new string[] {}
};
await signalRMessages.AddAsync(updateMessage);
}
Based on the logs that I added at different places in the Azure Functions, I see no errors and everything seemed to be called properly. However, I am not seeing any messages on signalR: the message count stays at 0 even if the connection count increases. My application does not received any messages.
Question
Why aren't the messages being sent to the Azure SignalR? What am I missing?
Wow.
The message seems to be dropped if the Arguments property is an empty list.
When I changed to Message instance to the following, it immediately worked:
var updateMessage = new SignalRMessage
{
GroupName = groupName,
Target = "notify",
Arguments = new[] { "Lulz" }
};

Subscribe to Outlook/Office 365 API Status Code NotFound in C#

I am getting an issue when creating subscription.
My steps are:
Register app at https://apps.dev.microsoft.com
Update permissions
Read Mail and User's info
Then update code, do same steps at https://learn.microsoft.com/en-us/outlook/rest/dotnet-tutorial
After login, I can get access token
Screen after login
Then I try to create a subscription for Inbox
var newSub = new Subscription
{
Resource = "me/mailFolders{'Inbox'}/messages",
ChangeType = "created,updated",
NotificationUrl = notificationUrl,
ClientState = clientState,
ExpirationDateTime = DateTime.Now.AddMinutes(15)
};
var result = await graphClient.Subscriptions.Request().AddAsync(newSub);
Implement for notification in notification URL - I can get validation token and return in plain text.
public async Task<ActionResult> Listen()
{
if (Request.QueryString["validationToken"] != null)
{
var token = Request.QueryString["validationToken"];
return Content(token, "plain/text");
}
}
But I always get this error.
Is there anyone know problem?
You must expose a public HTTPS endpoint to create a subscription and receive notifications from Microsoft Graph.

(13) The merchant login ID or password is invalid or the account is inactive

I want to implement Authorize .net payment gateway in my website using asp.net. I am a beginner in this. Can someone give me a sample code from where I can be redirected to the Authorize.net page to complete payment process.
I have created a sandbox account.
Redirection URL - https://test.authorize.net/gateway/transact.dll but I get an error
(13) The merchant login ID or password is invalid or the account is inactive.
My account is active and in test mode.
My Code:
protected void Button_pay_Click(object sender, EventArgs e)
{
string value = TextBox_amt.Text;
decimal d = decimal.Parse(value);
Run("abc", "abcq234", d);
}
public static ANetApiResponse Run(String ApiLoginID, String ApiTransactionKey, decimal amount)
{
Console.WriteLine("Create an Accept Payment Transaction Sample");
ApiOperationBase<ANetApiRequest, ANetApiResponse>.RunEnvironment = AuthorizeNet.Environment.SANDBOX;
// define the merchant information (authentication / transaction id)
ApiOperationBase<ANetApiRequest, ANetApiResponse>.MerchantAuthentication = new merchantAuthenticationType()
{
name = ApiLoginID,
ItemElementName = ItemChoiceType.transactionKey,
Item = ApiTransactionKey,
};
var opaqueData = new opaqueDataType
{
dataDescriptor = "COMMON.ACCEPT.INAPP.PAYMENT",
dataValue = "119eyJjb2RlIjoiNTBfMl8wNjAwMDUyN0JEODE4RjQxOUEyRjhGQkIxMkY0MzdGQjAxQUIwRTY2NjhFNEFCN0VENzE4NTUwMjlGRUU0M0JFMENERUIwQzM2M0ExOUEwMDAzNzlGRDNFMjBCODJEMDFCQjkyNEJDIiwidG9rZW4iOiI5NDkwMjMyMTAyOTQwOTk5NDA0NjAzIiwidiI6IjEuMSJ9"
};
var billingAddress = new customerAddressType
{
firstName = "John",
lastName = "Doe",
address = "123 My St",
city = "OurTown",
zip = "98004"
};
//standard api call to retrieve response
var paymentType = new paymentType { Item = opaqueData };
// Add line Items
var lineItems = new lineItemType[2];
lineItems[0] = new lineItemType { itemId = "1", name = "t-shirt", quantity = 2, unitPrice = new Decimal(15.00) };
lineItems[1] = new lineItemType { itemId = "2", name = "snowboard", quantity = 1, unitPrice = new Decimal(450.00) };
var transactionRequest = new transactionRequestType
{
transactionType = transactionTypeEnum.authCaptureTransaction.ToString(), // charge the card
amount = amount,
payment = paymentType,
billTo = billingAddress,
lineItems = lineItems
};
var request = new createTransactionRequest { transactionRequest = transactionRequest };
// instantiate the contoller that will call the service
var controller = new createTransactionController(request);
controller.Execute();
// get the response from the service (errors contained if any)
var response = controller.GetApiResponse();
//validate
if (response != null)
{
if (response.messages.resultCode == messageTypeEnum.Ok)
{
if (response.transactionResponse.messages != null)
{
Console.WriteLine("Successfully created transaction with Transaction ID: " + response.transactionResponse.transId);
Console.WriteLine("Response Code: " + response.transactionResponse.responseCode);
Console.WriteLine("Message Code: " + response.transactionResponse.messages[0].code);
Console.WriteLine("Description: " + response.transactionResponse.messages[0].description);
Console.WriteLine("Success, Auth Code : " + response.transactionResponse.authCode);
}
else
{
Console.WriteLine("Failed Transaction.");
if (response.transactionResponse.errors != null)
{
Console.WriteLine("Error Code: " + response.transactionResponse.errors[0].errorCode);
Console.WriteLine("Error message: " + response.transactionResponse.errors[0].errorText);
}
}
}
else
{
Console.WriteLine("Failed Transaction.");
if (response.transactionResponse != null && response.transactionResponse.errors != null)
{
Console.WriteLine("Error Code: " + response.transactionResponse.errors[0].errorCode);
Console.WriteLine("Error message: " + response.transactionResponse.errors[0].errorText);
}
else
{
Console.WriteLine("Error Code: " + response.messages.message[0].code);
Console.WriteLine("Error message: " + response.messages.message[0].text);
}
}
}
else
{
Console.WriteLine("Null Response.");
}
return response;
}
Aparajita you must check with the API credentials for sandbox in Authorize.Net account
This comes up often and the reasons are always the same:
You are using your production credentials but hitting the sandbox endpoints
You are using your sandbox credentials but are hitting the production endpoints
The credentials you are using are incorrect
Verifying which endpoint you are actually hitting.
If you are testing against the production environment, verify that the URL you have in your code is the actual production URL (or if you are using a framework, the configuration is set to production).
The correct URL for production is https://api2.authorize.net/xml/v1/request.api.
The correct URL for testing is https://apitest.authorize.net/xml/v1/request.api
Verify your credentials
If you are sure that you are hitting the correct endpoint you then need to verify that you are using the correct credentials for that environment.
Make sure you are using the API login and transaction key and not the console login and password. They are not the same thing. Only the API login and transaction key will work when accessing the APIs. You can get these after logging in to the console.
Double check that the API login is correct in the console.
If the API login is correct, from within the customer console generate a new transaction key to verify that you have the correct one.
Make sure that the credentials are not accidentally entered incorrectly in your code. Make sure all of the characters are there. Also, the credentials are case sensitive. Make sure you did not capitalize or lowercase those values.
If you need to verify your login credentials work set up a MVCE that hits the endpoint you are trying to reach with your API credentials. It should be a simple script that makes a basic API call. This will make it easy to debug why you are getting this error.
Test mode is not the sandbox
It is common to confuse test mode with the sandbox environment. Test mode in production uses the production environment and production credentials. Using the sandbox credentials or URLs will not work.
I used this sample project to solve my problem. This is in TEST Mode.
https://github.com/AuthorizeNet/sdk-dotnet/tree/master/CoffeeShopWebApp

Why entry.delete() is 400 when deleting google calendar entry/event?

Authenticating, searching, and adding events/entires to the google calendar is working as expected, but deleting results in a 400 bad request error.
The code is mostly copied from google's documentation.
Below googleUri is the link to the calendar entry (created by this same application/user, titled "Event To Delete") that should be removed and ConfigurationManager.AppSettings contains authentication information.
The debug output shows the calendar entry is found, yet deleting is not successful.
This uses Google Calendar API v2 which should still be functioning until 10/2014. Moving to v3 would be nice but, as far as I can tell, offers no way to authenticate with a known user+password (instead using expiring tokens that have require manually entering google credentials (?) ).
Debug.Write ("want to remove: " + googleURI);
// autheticate and get service
CalendarService svc = new CalendarService(ConfigurationManager.AppSettings["GoogleCalendarName"]);
svc.setUserCredentials(ConfigurationManager.AppSettings["GoogleCalendarUsername"], ConfigurationManager.AppSettings["GoogleCalendarPassword"]);
svc.QueryClientLoginToken();
// find the event(s) -- should only be one that can match the googleuri
EventQuery evtquery = new EventQuery(ConfigurationManager.AppSettings["GoogleCalendarPostURI"]);
evtquery.Uri = new Uri(googleURI);
EventFeed evtfeed = svc.Query (evtquery);
//should only be one event in the query
if (evtfeed == null || evtfeed.Entries.Count != 1) {
Debug.Write ("No match or too many matches for " + googleURI); // if this is less than 1, we should panic
return;
}
// we found the right one
EventEntry entry = (EventEntry)(evtfeed.Entries[0]);
Debug.Write ("Title: " + entry.Title.Text);
//hangs here until "The remote server returned an error: (400) Bad Request.
entry.Delete();
The output is:
[0:] want to remove: https://www.google.com/calendar/feeds/default/private/full/77e0tr0e3b4ctlirug30igeop0
[0:] Title: Event To Delete
I've also tried using the batch method to no avial
// https://developers.google.com/google-apps/calendar/v2/developers_guide_dotnet?csw=1#batch
// Create an batch entry to delete an the appointment
EventEntry toDelete = (EventEntry)calfeed.Entries[0];
toDelete.Id = new AtomId(toDelete.EditUri.ToString());
toDelete.BatchData = new GDataBatchEntryData("Deleting Appointment", GDataBatchOperationType.delete);
// add the entry to batch
AtomFeed batchFeed = new AtomFeed(calfeed);
batchFeed.Entries.Add(toDelete);
// run the batch
EventFeed batchResultFeed = (EventFeed)svc.Batch(batchFeed, new Uri(ConfigurationManager.AppSettings["GoogleCalendarPostURI"] ));
// check for succses
Debug.Write (batchResultFeed.Entries [0].BatchData.Status.Code);
if (batchResultFeed.Entries [0].BatchData.Status.Code != 200) {
return;
}
Couldn't figure out what was going on with v2, but I Was able to move to v3 of APIs using a Service Account to autheticate.
using System;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
// for BaseClientService.Initializer
using Google.Apis.Services;
// provides ServiceAccountCredential
using Google.Apis.Auth.OAuth2;
// read in cert
using System.Security.Cryptography.X509Certificates;
namespace LunaIntraDBCalTest
{
public class Program
{
public static string calendarname = "xxxx#gmail.com"; //address is default calendar
public static string serviceAccountEmail = "yyyy#developer.gserviceaccount.com";
public static CalendarService getCalendarService(){
// certificate downloaded after creating service account access at could.google.com
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
// autheticate
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "LunaIntraDB",
});
return service;
}
// create event object that will later be inserted
public static Event createEvent(string Summary, string Location, string Description, DateTime ApptDateTime, double duration ){
// create an event
Event entry= new Event
{
Summary = Summary,
Location = Location,
Description = Description,
Start = new EventDateTime { DateTime = ApptDateTime },
End = new EventDateTime { DateTime = ApptDateTime.AddHours(duration) },
};
return entry;
}
// capture event ID after inserting
public static string insertEvent(Event entry){
var service = getCalendarService ();
EventsResource.InsertRequest insert = service.Events.Insert (entry, calendarname);
// TODO: try catch here; will be throw exception if bad datetime
return insert.Execute().Id;
}
public static void deleteEvent(string id){
// TODO CATCH IF NOT FOUND
// to have access to this calendar, serviceAccountEmail needed permission set by ownner in google calendar
//Calendar cal1 = service.Calendars.Get (calnedarname).Execute();
var service = getCalendarService ();
service.Events.Delete (calendarname, id).Execute ();
}
public static void Main(string[] args)
{
// create event
var entry = createEvent ("TEST", "here", "this is a test", DateTime.Now, 1.5);
string id = insertEvent (entry);
Console.WriteLine("Run to your calendar to see that this event was created... (any key after checking)");
Console.ReadKey();
deleteEvent (id);
Console.WriteLine("Should now be deleted!... (any key to close)");
Console.ReadKey();
}
}
}

Categories

Resources