NMI Create Subscription Giving Authentication Failed Error - nmi-payments

In my project i am configuring NMI Payment gateway in which i create a plan and then customer and now creating subscription against that plan but client giving response of Authentication Failed below is the response of client
response=3&responsetext=Authentication Failed&authcode=&transactionid=0&avsresponse=&cvvresponse=&orderid=&type=&response_code=300
Below is my service code
var addsubscription = "add_subscription";
var date = DateTime.UtcNow;
var year = date.Year.ToString();
var month = date.Month.ToString();
var padedmonth = month.PadLeft(2, '0');
var day = date.Day.ToString();
var padedday = day.PadLeft(2, '0');
var startdate = year + padedmonth + padedday;
string option = $"plan_id={model.Data.StripePlanId}&recurring={addsubscription}&payment_token={model.Data.StripePaymentToken}&ccnumber={model.Data.CCNumber}&ccexp={model.Data.CCExpiry}&start_date={startdate}";
var requester = new NMIGatewayRequester();
var relativeUrl = "https://secure.networkmerchants.com/api/transact.php";
var response = requester.Request(relativeUrl, RestSharp.Method.POST, option);
var customerResponseObj = GetPaymentApiResponseValues(response.Split('&').Select(x => x.Split('=')).ToDictionary(x => x[0], x => x[1]));
public class NMIGatewayRequester
{
private RestClient client;
public string Request(string relativeUrl, RestSharp.Method verb, string option)
{
client = new RestSharp.RestClient($"{relativeUrl}") { Timeout = -1 };
var request = new RestRequest(verb);
request.AddParameter("application/x-www-form-urlencoded", option, ParameterType.RequestBody);
var subResponse = client.Execute(request);
if (!subResponse.IsSuccessful)
{
throw new Exception("Unable to Process Request");
}
return subResponse.Content;
}
}

You need to include a security key in your options:
string option = $"security_key=[...]&plan_id={model.Data.StripePlanId}&[...]";
See documentation here.

Related

Bad request when posting to OData Data Entity in Dynamics 365

I've created a public Data Entity in dynamics with the following fields:
I keep getting a bad request response, but I'm not sure why.
I've tried to make a POST request in two ways:
1.
HireAction hireAction = new HireAction() { CompanyName = "DEMF", MovieId = "DEMF-000000014", HireActionStatus = "Created" };
string jsonMessage = JsonConvert.SerializeObject(hireAction);
using (HttpClient client = new HttpClient())
{
HttpRequestMessage requestMessage = new
HttpRequestMessage(HttpMethod.Post, "MyDynamicsEnvironmentName/data/HireActions?cross-company=true");
requestMessage.Content = new StringContent(jsonMessage, Encoding.UTF8, "application/json");
requestMessage.Headers.Add("Authorization", AuthResult.AuthorizationHeader);
HttpResponseMessage response = client.SendAsync(requestMessage).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
//Logic
}
}
var url = "MyDynamicsEnvironmentName/data/HireActions?cross-company=true";
var req = HttpWebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/json";
req.Headers["Authorization"] = AuthResult.AuthorizationHeader;
HireAction hireAction = new HireAction() { CompanyName = "DEMF", MovieId = "DEMF-000000014", HireActionId = "12345", HireActionStatus = "Created" };
var jsonSettings = new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Local
};
var postString = "CompanyName='DEMF'" + "&MovieId='DEMF-000000014'" + "&HireActionId=132&HireActionStatus='Created'";
var data = JsonConvert.SerializeObject(postString, jsonSettings);
var bytes = Encoding.Default.GetBytes(postString);
var newStream = req.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();
using (var resp = req.GetResponse())
{
var results = new StreamReader(resp.GetResponseStream()).ReadToEnd();
}
Some keypoints:
-Of course you'd replace MyDynamicsEnvironmentName with the URL for the environment. The URL is correct and verified however, by the fact that GET requests do work
-The Authresult.AuthorizationHeader contains a valid token, also validated by working GET requests
As said before, both of these result in a bad request. Does someone know what is wrong or missing?

HTTP GET request to API behind AzureAD authentication with ASP.NET Core MVC

The code below gets a token which I then use to try and fetch some data from an API which is behind AzureAD authentication.
I get a token back, but when I use it to try and reach the API, I get "login to your account" in apiResponse.
What is wrong with my authorization?
var recoAadAppId = "xxxxxxxxxxxxxx";
var callerAadAppId = "xxxxxxxxxxxxxx";
var callerAadTenantId = "xxxxxxxxxxxxxx";
var token = await AcquireTokenWithSecret(callerAadAppId, callerAadTenantId, recoAadAppId);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(token.CreateAuthorizationHeader());
using (var response = await httpClient.GetAsync("https://redacted/app/rest/buildQueue"))
{
string apiResponse = await response.Content.ReadAsStringAsync();
}
public static Task<AuthenticationResult> AcquireTokenWithSecret(
string callerAadAppId, string callerTenantId, string recoAadAppId)
{
var secret = "mysecret";
var app = ConfidentialClientApplicationBuilder.Create(callerAadAppId).WithAuthority($"https://login.microsoftonline.com/{callerTenantId}").WithClientSecret(secret).Build();
var scopes = new[] { $"{recoAadAppId}/.default" };
return app.AcquireTokenForClient(scopes).ExecuteAsync(CancellationToken.None);
}

SharePoint Helper Class: WebRequest to HttpClient

I have a scenario where I need to use an OnPrem-SharePoint for File Upload/Download. It's SharePoint 2016 and I found a helper class from the old days that uses WebRequests to communicate. I must use the same class in my .net 7 Minimal API project. It works but I don't like the tech debt I have to carry by using WebRequests. There are quite a few methods there but I need help converting these main ones and I can manage the rest as they are somewhat copy-paste:
public partial class SharePointService : ISharePointService
{
private const string AuthUrl = "https://accounts.accesscontrol.windows.net/{0}/tokens/OAuth/2";
private readonly SPAppInit spAppInit;
public SharePointService(SPAppInit spAppInit) => this.spAppInit = spAppInit;
public SPAuthToken? GetAuthToken()
{
var postParameters = new[]
{
new KeyValuePair<string, string>("grant_type", spAppInit.GrantType),
new KeyValuePair<string, string>("client_id", $"{spAppInit.AppClientId}#{spAppInit.TenantId}"),
new KeyValuePair<string, string>("client_secret", spAppInit.AppSecret),
new KeyValuePair<string, string>("resource", $"00000000-0000-0aaa-ce00-000000000000/{spAppInit.TenantName}#{spAppInit.TenantId}"),
};
var postData = postParameters.Aggregate("", (current, t) => current + HttpUtility.UrlEncode(t.Key) + "=" + HttpUtility.UrlEncode(t.Value) + "&");
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(string.Format(AuthUrl, spAppInit.TenantId));
myHttpWebRequest.Method = "POST";
var data = Encoding.ASCII.GetBytes(postData);
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = data.Length;
var requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
var myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
var responseStream = myHttpWebResponse.GetResponseStream();
var myStreamReader = new StreamReader(responseStream, Encoding.Default);
var result = myStreamReader.ReadToEnd();
var authToken = JsonSerializer.Deserialize<SPAuthToken>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
myStreamReader.Close();
responseStream.Close();
myHttpWebResponse.Close();
return authToken;
}
public string GetRequestDigest(string authToken)
{
const string endpoint = "/_api/contextInfo";
var request = (HttpWebRequest)WebRequest.Create(spAppInit.SPSiteUrl + endpoint);
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {authToken}";
//request.Accept = "application/json;odata=verbose";
request.ContentLength = 0;
using var response = (HttpWebResponse)request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var result = reader.ReadToEnd();
// parse the ContextInfo response
var resultXml = XDocument.Parse(result);
// get the form digest value
var e = from r in resultXml.Descendants()
where r.Name == XName.Get("FormDigestValue", "http://schemas.microsoft.com/ado/2007/08/dataservices")
select r;
return e.First().Value;
}
public string CreateFolder(string authToken, string requestDigest, string folderName)
{
var request = (HttpWebRequest)WebRequest.Create($"{spAppInit.SPSiteUrl}/_api/web/getfolderbyserverrelativeurl('{spAppInit.SPDocLibServerRelativeUrl}/')/folders");
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {authToken}";
request.Headers["X-RequestDigest"] = requestDigest;
request.Accept = "application/json;";
request.ContentType = "application/json";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
var requestParams = CreateRequest(folderName);
var json = JsonSerializer.Serialize(requestParams);
streamWriter.Write(json);
}
using var response = (HttpWebResponse)request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var result = reader.ReadToEnd();
var folderCreationResponse = JsonSerializer.Deserialize<SPFolderCreationResponse>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return folderCreationResponse?.ServerRelativeUrl ?? string.Empty;
}
public void UploadLargeFile(string authToken, string formDigest, string sourcePath, string targetFolderUrl, int chunkSizeBytes = 2048)
{
using var client = new WebClient();
client.BaseAddress = spAppInit.SPSiteUrl;
client.Headers.Add("X-RequestDigest", formDigest);
client.Headers.Add("content-type", "application/json;odata=verbose");
client.Headers.Add("Authorization", $"Bearer {authToken}");
var fileName = Path.GetFileName(sourcePath);
var createFileRequestUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFolderByServerRelativeUrl('{targetFolderUrl}')/files/add(url='{fileName}',overwrite=true)";
client.UploadString(createFileRequestUrl, "POST");
var targetUrl = Path.Combine(targetFolderUrl, fileName);
var firstChunk = true;
var uploadId = Guid.NewGuid();
var offset = 0L;
using var inputStream = File.OpenRead(sourcePath);
var buffer = new byte[chunkSizeBytes];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (firstChunk)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/startUpload(uploadId=guid'{uploadId}')";
client.UploadData(endpointUrl, buffer);
firstChunk = false;
}
else if (inputStream.Position == inputStream.Length)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/finishUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
var finalBuffer = new byte[bytesRead];
Array.Copy(buffer, finalBuffer, finalBuffer.Length);
client.UploadData(endpointUrl, finalBuffer);
}
else
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/continueUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
client.UploadData(endpointUrl, buffer);
}
offset += bytesRead;
Console.WriteLine("%{0:P} completed", offset / (float)inputStream.Length);
}
}
public void UploadLargeFileUsingBytes(string authToken, string formDigest, string fileName, byte[] fileBytes, string targetFolderUrl, int chunkSizeBytes = 2048)
{
if (fileBytes.Length < chunkSizeBytes)
{
UploadSmallFile(authToken, formDigest, targetFolderUrl, fileName, fileBytes);
}
else
{
using var client = new WebClient();
client.BaseAddress = spAppInit.SPSiteUrl;
client.Headers.Add("X-RequestDigest", formDigest);
client.Headers.Add("content-type", "application/json;odata=verbose");
client.Headers.Add("Authorization", $"Bearer {authToken}");
var createFileRequestUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFolderByServerRelativeUrl('{targetFolderUrl}')/files/add(url='{fileName}',overwrite=true)";
client.UploadString(createFileRequestUrl, "POST");
var targetUrl = Path.Combine(targetFolderUrl, fileName);
var firstChunk = true;
var uploadId = Guid.NewGuid();
var offset = 0L;
using var inputStream = ReadFileStreamFromByte(fileBytes);
var buffer = new byte[chunkSizeBytes];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (firstChunk && inputStream.Length > chunkSizeBytes)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/startUpload(uploadId=guid'{uploadId}')";
client.UploadData(endpointUrl, buffer);
firstChunk = false;
}
else if (inputStream.Position == inputStream.Length)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/finishUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
var finalBuffer = new byte[bytesRead];
Array.Copy(buffer, finalBuffer, finalBuffer.Length);
client.UploadData(endpointUrl, finalBuffer);
}
else
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/continueUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
client.UploadData(endpointUrl, buffer);
}
offset += bytesRead;
Console.WriteLine("%{0:P} completed", offset / (float)inputStream.Length);
}
}
}
}
I don't like pasting the whole project. However, even after being very selective, this is still a lot of code, but its communication code that I do not understand as I started with the newer HttpClient only as (some random HttpClient injection in my API):
services.AddCustomHttpClient<ILetterServiceApi, LetterServiceApi>(Constants.ApiServiceNamedClientLetter, baseAddress!);
Where AddCustomHttpClient is,
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomHttpClient<TInterface, TImplementation>(this IServiceCollection services, string serviceName, string baseAddress)
where TInterface: class
where TImplementation: class, TInterface
{
services.AddHttpClient<TInterface, TImplementation>(serviceName, config =>
{
config.BaseAddress = new Uri(baseAddress);
config.Timeout = new TimeSpan(0, 0, 30);
})
.AddHttpMessageHandler<RequestHandler>();
return services;
}
}
I have made a lot of modern modifications to the code and also Am in the process of converting this class (in a remote assembly) to conform to the .Net Core's .AddSharePoint() DI pattern, and for that, I need to get rid of obsolete (HttpWebRequest)WebRequest
Update:
I managed to convert a rather simple method:
private SPFolderResponse? GetFolderFiles(string authToken, string requestDigest, string folderName)
{
using var client = new HttpClient();
var serverRelativeFolderUrl = $"{spAppInit.RelativeUrl}/{folderName}";
var folderApiUrl = $"{spAppInit.SiteUrl}/_api/web/GetFolderByServerRelativeUrl('/{serverRelativeFolderUrl}')/Files";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-RequestDigest", requestDigest);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {authToken}");
var result = client.GetStringAsync(folderApiUrl).GetAwaiter().GetResult();
var response = JsonSerializer.Deserialize<SPFolderResponse>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return response;
}
But the other ones like the Token, Digest and Upload ones are still dodgy...

How to create a Stripe Invoice for a connect account on a shared customer

I have successfully created a customer on my Stripe platform account. Now I want to create an invoice for that customer from one of my connect accounts.
The connect account delivered a service to the customer and now will be invoicing the customer for payment - I am using the Stripe Invoice capability.
However, I am getting the error "No such customer: cus_XXXX" even after following examples to associate the connect account with the customer account.
I have followed https://stripe.com/docs/connect/shared-customers#making-tokens to create a token for my platform customer and then use that token to create a connect account customer as indicated at https://lorenzosfarra.com/2017/09/19/stripe-shared-customers/ but am still getting the same error message.
Even though it seems that the connect account reference is created successfully - as there are no errors returned, the resulting customer ID is not found.
Here is my Create Invoice Method
public static async Task<string> CreateCustomerInvoice(string secretKey, string customerId, TenantBillHeaderModel billHeader)
{
StripeConfiguration.ApiKey = secretKey;
var fees = await Database.GetAdminFees();
var salesTax = await Database.GetCountrySalesTax(13);// Hardcoded for launch
var billLines = await Database.GetTenantBillDetailForHeaderId(billHeader.TenantBillHeaderId);
var stripeContact = await Database.GetStripeContact(UserInfo.Instance.Organisation.OrganisationId);
InvoiceItemCreateOptions invoiceItemOptions = null;
// Find Fee Percentage
var tFee = billHeader.GrossAmount * (fees.FirstOrDefault(f => f.AdminFeeId == (int)Persistence.Enums.AdminFeeEnum.ConnectCut)).Percentage / 100;
// Create token so that customer can be shared with Connect account - See https://stripe.com/docs/connect/shared-customers
var customerTokenId = await CreateCustomerToken(secretKey, customerId, stripeContact.StripeConnectToken);
//Associates customer with connect account so that it is shared with platform account
var connectCustId = await CreateCustomerInConnectAccount(secretKey, customerTokenId, stripeContact.StripeConnectToken);
foreach (var l in billLines)
{
invoiceItemOptions = new InvoiceItemCreateOptions
{
Quantity = l.Quantity,
UnitAmount = Convert.ToInt64(l.Amount * 100), // Converting to cents for Stripe
Currency = "aud", // Hardcoded for launch
CustomerId = connectCustId,
Description = l.Name + " (" + l.Description + ")",
TaxRates = new List<string> {
salesTax.StripeTaxRateId
},
};
var itemService = new InvoiceItemService();
InvoiceItem invoiceItem = await itemService.CreateAsync(invoiceItemOptions);
}
var invoiceOptions = new InvoiceCreateOptions
{
CustomerId = connectCustId,
AutoAdvance = false, // do not auto-finalize this draft after ~1 hour
CollectionMethod = "send_invoice", // Stripe will progress the a send state. However it will not email as this is trurned off in portal https://stripe.com/docs/billing/invoices/customizing
ApplicationFeeAmount = Convert.ToInt64(tFee),
Description = "Invoice for Advizr Bill: " + billHeader.BillNumber,
DueDate = billHeader.DueDate
};
var service = new InvoiceService();
Invoice invoice = await service.CreateAsync(invoiceOptions);
return invoice.Id;
}
Here I create a customer token
public static async Task<string> CreateCustomerToken(string secretKey, string customerId, string stripeContactId)
{
StripeConfiguration.ApiKey = secretKey;
RequestOptions requestOptions = null;
var options = new TokenCreateOptions
{
CustomerId = customerId,
};
if (stripeContactId != null)
{
requestOptions = new RequestOptions
{
StripeAccount = stripeContactId
};
}
var service = new TokenService();
Token token = await service.CreateAsync(options, requestOptions);
return token.Id;
}
and here I create / associate the customer with the connect account
public static async Task<string> CreateCustomerInConnectAccount(string secretKey, string customerToken, string connectAccount)
{
StripeConfiguration.ApiKey = secretKey;
var options = new CustomerCreateOptions
{
Source = customerToken
};
var requestOptions = new RequestOptions
{
StripeAccount = connectAccount
};
var service = new CustomerService();
Customer customer = await service.CreateAsync(options, requestOptions);
return customer.Id;
}
Any guidance on how to create an invoice for a shared customer (Exists in platform account and is associated with the connect account) will be appreciated.
The customer will then pay the invoice and I will use a destination to pay the connect account after deducting the Application Fee.
Thanks
After logging and issue with Stripe Support, they provided me with the answer.
To resolve this issue, the connect Account
var requestOptions = new RequestOptions
{
StripeAccount = stripeContact.StripeConnectToken
};
must be provided when creating an invoice line item
var itemService = new InvoiceItemService();
InvoiceItem invoiceItem = await itemService.CreateAsync(invoiceItemOptions, requestOptions);
and when creating the invoice.
var service = new InvoiceService();
Invoice invoice = await service.CreateAsync(invoiceOptions, requestOptions);
Hope this helps someone.

AcquireTokenByAuthorizationCodeAsync does not complete

Here is my code. It's been over a month I'm trying to add calendars in Outlook but nothing is working :( please help. The function AcquireTokenByAuthorizationCodeAsync never completes. And the token.Result is always null
string authority = ConfigurationManager.AppSettings["authority"];
string clientID = ConfigurationManager.AppSettings["clientID"];
Uri clientAppUri = new Uri(ConfigurationManager.AppSettings["clientAppUri"]);
string serverName = ConfigurationManager.AppSettings["serverName"];
var code = Request.Params["code"];
AuthenticationContext ac = new AuthenticationContext(authority, true);
ClientCredential clcred = new ClientCredential(clientid, secretkey);
//ac = ac.AcquireToken(serverName, clientID, clientAppUri, PromptBehavior.Auto);
//string to = ac.AcquireToken(serverName, clientID, clientAppUri, PromptBehavior.Auto).AccessToken;
var token = ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:2694/GetAuthCode/Index/"), clcred, resource: "https://graph.microsoft.com/");
string newtoken = token.Result.AccessToken;
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = new Uri("https://outlook.office365.com/" + "ews/exchange.asmx");
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(token.Result.AccessToken);
exchangeService.FindFolders(WellKnownFolderName.Root, new FolderView(10));
Appointment app = new Appointment(exchangeService);
app.Subject = "";
app.Body = "";
app.Location = "";
app.Start = DateTime.Now;
app.End = DateTime.Now.AddDays(1);
app.Save(SendInvitationsMode.SendToAllAndSaveCopy);
I have experienced this issue when I call the AcquireTokenByAuthorizationCodeAsync using result instead of using await key words and all these code was in a asynchronously controller in the MVC.
If you are in the same scenario, you can fix this issue by two ways:
1.Always using the async, await like code below:
public async System.Threading.Tasks.Task<ActionResult> About()
{
...
var result =await ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:2694/GetAuthCode/Index/"), clcred, resource: "https://graph.microsoft.com/");
var accessToken = result.AccessToken;
...
}
2.Use the synchronization controller:
public void About()
{
...
var result =ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:2694/GetAuthCode/Index/"), clcred, resource: "https://graph.microsoft.com/").Result;
var accessToken = result.AccessToken;
...
}

Resources