Chained payments with permissions 520003 Authentication failed - asp.net-mvc

I have problem with executing chained payment after obtaining permission from user. I have no troubles to get Access Token and Access Token Secret.
The code should work this way: User (personal type sandbox account) authorizes primary receiver (business type sandbox account) to take money from his account.
Primary receiver sends a part of the sum to the second receiver.
The code is written in ASP.NET MVC but I suppose it has nothing to do with the framework, I'm using PayPalAdaptivePaymentsSDK and PayPalPermissionsSDK
I want to use it in sandbox environment for testing.
I assume it has something to do with application id, I would be very grateful for step-by-step explanation
Here's how I obtain all my credentials:
I login into my developer account.
account1.Username, account1.Password, account1.Signature
- sandbox -> accounts -> I select primary receiver of the payment (business type account) -> API Credentials
applicationId - "APP-80W284485P519543T"
In the config file I specified mode as "sandbox".
I use the same config for executing the payment.
Then I want to execute following code and in payResponse I get errorId 520003 "Authentication failed. API credentials are incorrect."
public class PermissionController : Controller
{
private AdaptivePaymentsService _adaptivePaymentService;
public ActionResult Index()
{
ViewBag.AccessToken = PayPalConfig.AccessToken;
ViewBag.AccessTokenSecret = PayPalConfig.AccessTokenSecret;
return View();
}
public PermissionController()
{
_adaptivePaymentService = new AdaptivePaymentsService(PayPalConfig.GetConfig());
}
public ActionResult GetPermission()
{
RequestPermissionsRequest rp = new RequestPermissionsRequest();
rp.scope = new List<string>();
rp.scope.Add("EXPRESS_CHECKOUT");
Dictionary<string, string> config = PayPalConfig.GetConfig();
rp.callback = "http://localhost:42072/Permission/GetAccessToken";
rp.requestEnvelope = new PayPal.Permissions.Model.RequestEnvelope("en_US");
RequestPermissionsResponse rpr = null;
PermissionsService service = new PermissionsService(config);
rpr = service.RequestPermissions(rp);
string confirmPermissions = "https://www.sandbox.paypal.com/webscr&cmd=_grant-permission&request_token=" + rpr.token;
return Redirect(confirmPermissions);
}
public ActionResult GetAccessToken()
{
Uri uri = Request.Url;
Dictionary<string, string> config = PayPalConfig.GetConfig();
var gat = new GetAccessTokenRequest();
gat.token = HttpUtility.ParseQueryString(uri.Query).Get("request_token");
gat.verifier = HttpUtility.ParseQueryString(uri.Query).Get("verification_code");
gat.requestEnvelope = new PayPal.Permissions.Model.RequestEnvelope("en_US");
GetAccessTokenResponse gats = null;
var service = new PermissionsService(config);
gats = service.GetAccessToken(gat);
_adaptivePaymentService.SetAccessToken(gats.token);
_adaptivePaymentService.SetAccessTokenSecret(gats.tokenSecret);
PayPalConfig.AccessToken = _adaptivePaymentService.getAccessToken();
PayPalConfig.AccessTokenSecret = _adaptivePaymentService.getAccessTokenSecret();
return RedirectToAction("Index");
}
public ActionResult ChainedPayment()
{
ReceiverList receiverList = new ReceiverList();
receiverList.receiver = new List<Receiver>();
Receiver secondaryReceiver = new Receiver(1.00M);
secondaryReceiver.email = "mizerykordia6662-facilitator#gmail.com";
receiverList.receiver.Add(secondaryReceiver);
Receiver primaryReceiver = new Receiver(5.00M);
primaryReceiver.email = "mizTestMerchant#test.com";
primaryReceiver.primary = true;
receiverList.receiver.Add(primaryReceiver);
PayPal.AdaptivePayments.Model.RequestEnvelope requestEnvelope = new PayPal.AdaptivePayments.Model.RequestEnvelope("en_US");
string actionType = "PAY";
string returnUrl = "http://localhost:42072/Home/Index";
string cancelUrl = "https://devtools-paypal.com/guide/ap_chained_payment/dotnet?cancel=true";
string currencyCode = "USD";
_adaptivePaymentService.SetAccessToken(PayPalConfig.AccessToken);
_adaptivePaymentService.SetAccessTokenSecret(PayPalConfig.AccessTokenSecret);
PayRequest payRequest = new PayRequest(requestEnvelope, actionType,
cancelUrl, currencyCode, receiverList, returnUrl);
// it breaks here
PayResponse payResponse = _adaptivePaymentService.Pay(payRequest);
PaymentDetailsRequest paymentDetailsRequest = new PaymentDetailsRequest(new PayPal.AdaptivePayments.Model.RequestEnvelope("en-US"));
paymentDetailsRequest.payKey = payResponse.payKey;
SetPaymentOptionsRequest paymentOptions = new SetPaymentOptionsRequest()
{ payKey = payResponse.payKey };
_adaptivePaymentService.SetPaymentOptions(paymentOptions);
PaymentDetailsResponse paymentDetailsRespons = _adaptivePaymentService.PaymentDetails(paymentDetailsRequest);
ExecutePaymentRequest exec = new ExecutePaymentRequest(new PayPal.AdaptivePayments.Model.RequestEnvelope("en-US"), paymentDetailsRequest.payKey);
ExecutePaymentResponse response = _adaptivePaymentService.ExecutePayment(exec);
return RedirectToAction("Index");
}
}

Related

Create team in GraphAPI returns always null

I am using GraphAPI SDK to create a new Team in Microsoft Teams:
var newTeam = new Team()
{
DisplayName = teamName,
Description = teamName,
AdditionalData = new Dictionary<string, object>()
{
{"template#odata.bind", "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"}
},
Members = new TeamMembersCollectionPage()
{
new AadUserConversationMember
{
Roles = new List<String>()
{
"owner"
},
AdditionalData = new Dictionary<string, object>()
{
{"user#odata.bind", $"https://graph.microsoft.com/v1.0/users/{userId}"}
}
}
}
};
var team = await this.graphStableClient.Teams
.Request()
.AddAsync(newTeam);
The problem is that I get always null. According documentation this method returns a 202 response (teamsAsyncOperation), but the AddAsync method from SDK returns a Team object. Is there any way to get the tracking url to check if the team creation has been finished with the SDK?
Documentation and working SDK works different... As they wrote in microsoft-graph-docs/issues/10840, we can only get the teamsAsyncOperation header values if we use HttpRequestMessage as in contoso-airlines-teams-sample. They wrote to the people who asks this problem, look to the joined teams :)) :)
var newTeam = new Team()
{
DisplayName = model.DisplayName,
Description = model.Description,
AdditionalData = new Dictionary<string, object>
{
["template#odata.bind"] = $"{graph.BaseUrl}/teamsTemplates('standard')",
["members"] = owners.ToArray()
}
};
// we cannot use 'await client.Teams.Request().AddAsync(newTeam)'
// as we do NOT get the team ID back (object is always null) :(
BaseRequest request = (BaseRequest)graph.Teams.Request();
request.ContentType = "application/json";
request.Method = "POST";
string location;
using (HttpResponseMessage response = await request.SendRequestAsync(newTeam, CancellationToken.None))
location = response.Headers.Location.ToString();
// looks like: /teams('7070b1fd-1f14-4a06-8617-254724d63cde')/operations('c7c34e52-7ebf-4038-b306-f5af2d9891ac')
// but is documented as: /teams/7070b1fd-1f14-4a06-8617-254724d63cde/operations/c7c34e52-7ebf-4038-b306-f5af2d9891ac
// -> this split supports both of them
string[] locationParts = location.Split(new[] { '\'', '/', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
string teamId = locationParts[1];
string operationId = locationParts[3];
// before querying the first time we must wait some secs, else we get a 404
int delayInMilliseconds = 5_000;
while (true)
{
await Task.Delay(delayInMilliseconds);
// lets see how far the teams creation process is
TeamsAsyncOperation operation = await graph.Teams[teamId].Operations[operationId].Request().GetAsync();
if (operation.Status == TeamsAsyncOperationStatus.Succeeded)
break;
if (operation.Status == TeamsAsyncOperationStatus.Failed)
throw new Exception($"Failed to create team '{newTeam.DisplayName}': {operation.Error.Message} ({operation.Error.Code})");
// according to the docs, we should wait > 30 secs between calls
// https://learn.microsoft.com/en-us/graph/api/resources/teamsasyncoperation?view=graph-rest-1.0
delayInMilliseconds = 30_000;
}
// finally, do something with your team...
I found a solution from another question... Tried and saw that it's working...

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.

EWS OAuth .net core 2.1

We have a solution today where we use EWS's basic authentication (username and password) with .net Core 2.1, and it works. The problem is that basic authentication will expire in 2020. Therefore, we will transition to the OAuth solution that will work after 2020.
We have tried multiple solutions for this problem, including this: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth, but some of the methods have been updated (AcquireToken -> AcquireTokenAsync).
It's important that the authentication against azure is not client-based, since everything will happen in backend (web api).
Does anyone have a solution to this problem?
This is our current solution:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials(<email>, <password>);
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
This is an example of what we have tried:
public class Program
{
public static void Run()
{
//tried this as well: string authority = "https://login.windows.net/<devAccountName>.onmicrosoft.com";
string authority = "https://login.microsoftonline.com/<tenantId>/OAuth2/Token";
string clientId = "<clientId>"; // Application ID from Azure
Uri clientAppUri = new Uri("http://localhost:55424/");
Uri resourceHostUri = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
AuthenticationResult authenticationResult = null;
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
string errorMessage = null;
try
{
Console.WriteLine("Trying to acquire token");
PlatformParameters platformParams = new PlatformParameters(PromptBehavior.Auto);
authenticationResult = authenticationContext.AcquireTokenAsync("https://outlook.office365.com/EWS/Exchange.asmx", clientId, clientAppUri, platformParams).Result;
}
catch (AdalException ex)
{
errorMessage = ex.Message;
if (ex.InnerException != null)
{
errorMessage += "\nInnerException : " + ex.InnerException.Message;
}
}
catch (ArgumentException ex)
{
errorMessage = ex.Message;
}
if (!string.IsNullOrEmpty(errorMessage))
{
Console.WriteLine("Failed: {0}" + errorMessage);
return;
}
Console.WriteLine("\nMaking the protocol call\n");
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = resourceHostUri;
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
exchangeService.FindFolders(WellKnownFolderName.Root, new FolderView(10));
}
}
We receive this error message after we log in:
AADSTS50001: The application named
https://outlook.office365.com/EWS/Exchange.asmx was not found in the tenant named <tenantId>. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.

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;
...
}

401 error calling REST service from mvc controller

I am trying to make a request to a REST method and recieve the data in XML but this all goes well but when i want to use credentials for a method, because for this method you need to give credentials.
https://handshake:16a144bc5f480692d5c8d926068d2db5#rest-api.pay.nl/v2/Transaction/getStatus/xml/?orderId=236750347X6d2ee7
But when i use this one in the browser it work but this is not working from my controller.
//
// GET: /Home/Succes
public ActionResult Succes(string orderId)
{
string token = PaymentCalls.Login();
ViewBag.Token = token;
string _URL = "https://handshake:" + token + "#rest-api.pay.nl/v2/Transaction/getStatus/xml/?orderId=" + orderId;
NetworkCredential cr = new NetworkCredential("handshake", token);
XDocument doc = XDocument.Load(_URL);
var output = from feed in doc.Descendants("data")
select new Status
{
amount = feed.Element("amount").Value,
consumerAccountNumber = feed.Element("consumerAccountNumber").Value,
consumerCity = feed.Element("consumerCity").Value,
consumerEmail = feed.Element("consumerEmail").Value,
consumerName = feed.Element("consumerName").Value,
consumerZipcode = feed.Element("consumerZipcode").Value,
countryCode = feed.Element("countrCode").Value,
entranceCode = feed.Element("entranceCode").Value,
ipAddress = feed.Element("ipAddress").Value,
orderId = feed.Element("orderId").Value,
statsAdded = feed.Element("statsAdded").Value,
paymentSessionId = feed.Element("paymentSessionId").Value,
result = feed.Element("result").Value,
statusAction = feed.Element("statusAction").Value
};
return View(output);
So when we call this controller we get an 401 error but using the same url in the browser works. So i dont know but how can i set to pass credentials or something?

Resources