I'm attempting to capture a PayPal transaction that has been authorized using the PayPal button. I'm trying to use CyberSource Simple Order API to do this. I have the only 3 pieces of information that seem to come back from the PayPal button are: payerID, paymentID and paymentToken. I've tried a few ways of handing this off to the Simple Order API, but always get a 102 code with the DECLINE message in the response. Cybersource's logging system indicates this is because The following request field(s) is either invalid or missing: request_token.
Do I need to conduct the whole transaction - authorize and capture - via cybersource? Or what is the way I can take the paypal-generated button and authorize a transaction, then capture it via CyberSource?
Here's my code snippet for the CyberSource SOAPI request:
RequestMessage request = new RequestMessage
{
merchantID = WebConfigurationManager.AppSettings["cybs.merchantID"]
, payPalDoCaptureService = new PayPalDoCaptureService {
run = "true"
, invoiceNumber = orders
, paypalAuthorizationId = authId
, paypalAuthorizationRequestToken = requestToken
, completeType = "Complete" }
, clientApplication = "MyClient Application"
, clientApplicationVersion = "2.0"
, clientApplicationUser = userName
, clientEnvironment = WebConfigurationManager.AppSettings["Tier"]
, merchantReferenceCode = orders
, customerID = OrderConstants.CustomerNumber
, merchantDefinedData = new MerchantDefinedData { field1 = "Customer #: " + OrderConstants.CustomerNumber, field2 = orders }
, purchaseTotals = new PurchaseTotals { currency = "usd", grandTotalAmount = total, taxAmount = taxtotal }
, item = items.ToArray()
};
ReplyMessage reply = new ReplyMessage();
try
{
reply = SoapClient.RunTransaction(request);
}
catch (Exception ex)
{
reply.decision = "SYSTEM ERROR";
reply.additionalData = string.Format("Error processing request. Exception message: {0}", ex.Message);
}
Have you tried setting up your request like this? Pay no attention to the values I used, but I am looking at the example in the docs (Example 21 Request for payPalDoCaptureService). You'll need to ctrl+f to find it.
RequestMessage request = new RequestMessage
{
payPalDoCaptureService_run=true
, merchantID = WebConfigurationManager.AppSettings["cybs.merchantID"]
, merchantReferenceCode = HTNsubscription9647
, purchaseTotals = new PurchaseTotals { currency = "usd", grandTotalAmount = total, taxAmount = taxtotal }
, payPalDoCaptureService = new PayPalDoCaptureService
{
run = "true"
, invoiceNumber = orders
, paypalAuthorizationId = authId
, paypalAuthorizationRequestID = authReqId
, paypalAuthorizationRequestToken = requestToken
, completeType = "Complete"
}
}
I did end up solving this problem. My question involving whether I needed to employ CyberSource from end-to-end was the correct path. Eventually I figured out - after talking for days with CyberSource support - that I needed to use their "Set Service" in a WebApi method to generate the PayPal "token" and give that to the PayPal button via ajax within the javascript that generates the button. I also had to dig deep into their and PayPal's documentation and to figure out how to make that work because it wasn't super clear initially. At any rate, once I did the Set service, I gathered the information in the response from CyberSource, along with the PayPal token, and send that along to a Get Details service call (also CyberSource). Finally that information is given to a combined Do Sale and Do Capture service call and the funds are captured. The button only allowed a user to log into their PayPal account and verify they wanted to continue with the transaction.
CyberSource's documentation is a little lacking in clarity with regards to this, and they link to a deprecated set of PayPal documentation, which doesn't make anything easier.
As the error says you have git invalid data, please check the reply fields invalidField_0 through invalidField_N for
the invalid fields.
I hope these Requesting the Authorization Service samples will help
Related
I have an application that uses a deep integration with Stripe. My platform is occasionally charging the connected accounts and I am storing information about these charges in the metadata of the charge itself.
I want to display this information back to the connected account so I am using the Charge Service to list charges. However I want to filter that list based on some metadata key/value pair so that I don't have to list all of the charges each time, for every connected account.
Is there a clever way of doing this?
Filtering based on metadata is not supported.
Since you are doing this across Connected accounts, a better approach would be to store the Charge ID and metadata on your end so that you don't have to list and paginate through charges, looking for particular metadata.
Stripe recently added Search API that allows search by metadata. Currently, it does not have .NET / C# integration but you can query using HTTP call directly:
Example from their docs:
curl https://api.stripe.com/v1/search/charges \
-u sk_test_daHanKyJOCiXCeBsa65biLML00wylimZ8S: \
--data-urlencode "query=metadata['key']:'value'" \
-H "Stripe-Version: 2020-08-27;search_api_beta=v1" \
-G
I was able to accomplish this with the "TransferGroup" property of the ChargeCreateOptions object. I basically set the TransferGroup property to a unique value for each connected account which I can then query later.
CREATE THE CHARGE
//THERE IS NO ID IN THE REQUEST OPTIONS BECAUSE
//WE ARE USING THE PLATFORM
var requestOptions = new RequestOptions()
{
ApiKey = {API Key}
};
//CHARGE OPTIONS
var chargeOptions = new ChargeCreateOptions
{
Amount = {AMOUNT},
Currency = "usd",
Description = $"Platform Charge",
SourceId = {StripeId},
Metadata = {MetaData},
TransferGroup = {Unique String For Each Connected Account}
};
//CREAT AND RETURN THE CHARGE
var chargeService = new ChargeService();
return chargeService.Create(chargeOptions, requestOptions);
QUERY THE PLATFORM CHARGES BASED ON TRANSFER GROUP
public List<Charge> StripePlatformCharges(string apiKey, int days, string transferGroup)
{
try
{
RequestOptions requestOptions = new RequestOptions()
{
ApiKey = apiKey
};
ChargeListOptions chargeListOptions = new ChargeListOptions()
{
CreatedRange = new DateRangeOptions()
{
GreaterThanOrEqual = DateTime.Now.AddDays(-days)
},
TransferGroup = transferGroup
};
ChargeService chargeService = new ChargeService();
return chargeService.List(chargeListOptions, requestOptions).ToList();
}
catch (Exception ex)
{
throw new Exception("Error getting stripe charges from the platform", ex);
}
}
Stripe recently released a Search API which offers filtering by metadata for certain resources including charges. The documentation is at https://stripe.com/docs/search
I'm running a Stripe test payment on an ASP Net site. When the user is ready to pay they are FORWARDED to their gateway to collect card details.
Once they complete a successful payment they are returned to the success URL i set with the {SESSION_ID} variable alongside the URL i.e. www.example.com/success?session_id{Session_ID}.
I then process the payment by getting the session first
StripeConfiguration.ApiKey = "sk_test_123";
var service = new SessionService();
Checkout.Session sess service.Get("cs_test_4561");
I then find the payment from my database using the session id i saved and passed when the user checked out.
My concern is that through a web sniffer tool the Session ID can be picked up and you can pass that manually to the successURL.
I can add HTTPS to the site but is there anything else i could do to make this a little more difficult i.e. expire the Session ID after some time or have a value from Strip to confirm the payment is successful?
Edit 1:
My code behind to set the SessionCreateOptions
var options = new SessionCreateOptions
{
PaymentMethodTypes = new List<string> { "card", },
LineItems = GetItems(),
SuccessUrl = "www.example.com/success?session_id={CHECKOUT_SESSION_ID}",
CancelUrl = "www.example.com/cancel",
PaymentIntentData = new SessionPaymentIntentDataOptions
{
Metadata = new Dictionary<string, string>{
{"orderID","123"}
}
},
Mode="payment",
};
var service = new SessionService();
Session session = service.Create(options);
When the user is returned back to the success page, i run this code to get the session i run the above code to get the Session but payment_intent is null if i type sess.PaymentIntent
When you retrieve the CheckoutSession by its id, you should check its payment_intent to verify that it shows status: succeeded. That shows that the payment went through without issue.
More details on the entire process here:
https://stripe.com/docs/payments/checkout/fulfillment
I am trying to broadcast live from my .Net application using Youtube.Data.Api v3.
I have set up OAuth and downloaded the .JSON file, and that works fine. I know that, because I have already successfully obtained a list of channels resp. videos on my account, i.e., following code works:
var channelsRequest = ytService.Channels.List("contentDetails, snippet");
channelsRequest.Mine = true;
var channelsListResponse = channelsRequest.Execute();
But if I try to execute a insert request (for completeness I show you the whole method),
public static LiveBroadcast CreateImmediateBroadcast(string title = "DefaultBroadcast") {
var snippet = new LiveBroadcastSnippet();
snippet.Title = title;
snippet.ScheduledStartTime = DateTime.Now;
snippet.ScheduledEndTime = DateTime.Now + TimeSpan.FromMinutes(60);
var status = new LiveBroadcastStatus();
status.PrivacyStatus = "unlisted";
var broadcast = new LiveBroadcast();
broadcast.Kind = "youtube#liveBroadcast";
broadcast.Snippet = snippet;
broadcast.Status = status;
var insertBroadcastRequest = ytService.LiveBroadcasts.Insert(broadcast, "snippet, status");
insertBroadcastRequest.Execute();
return broadcast;
}
I get an exception when calling insertBroadcastRequest.Execute(), namely:
Google.GoogleApiException was unhandled
HResult=-2146233088
Message=Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [
Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]
]
ServiceName=youtube
Source=Google.Apis
StackTrace:
at Google.Apis.Requests.ClientServiceRequest`1.Execute() in C:\Users\cloudsharp\Documents\GitHub\google-api-dotnet-client\Src\Support\GoogleApis\Apis\Requests\ClientServiceRequest.cs:line 96
at YoutubeConsole.YouTubeAPI.CreateImmediateStream(String title) in C:\Users\bussg\Source\Workspaces\OwnExperimental\YoutubeConsole\YoutubeConsole\YouTubeAPI.cs:line 87
at YoutubeConsole.YouTubeAPI.Test() in
...
Also, for completeness, here is my authorization,
using (var stream = new FileStream(Directory.GetCurrentDirectory() + #"\GoogleAuthOtherApplication.json", FileMode.Open, FileAccess.Read)) {
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeForceSsl},
"user",
CancellationToken.None,
new FileDataStore("YouTubeAPI")
).Result;
}
Also, For the YouTubeService.Scope I have tried all options. The insert method should work with ForceSsl according to the documentation.
Also this documentation page sais
Note: A channel must be approved to use the YouTube Live feature, which enables the channel owner to stream live content to that channel. If you send API requests on behalf of an authenticated user whose channel is not enabled or eligible to stream live content, the API will return an insufficientPermissions error.
But all my channels are approved for Youtube Live. Any ideas how to get this to work?
Ok after some testing between us over Email.
You need to have the correct scope "YouTubeService.Scope.YoutubeForceSsl" by changing "user" we forced it to request permissions again. My tutorial on how filedata store works in the Google .net client library
remove the space "snippet, status" by sending "snippet,status" it worked for me.
For the fun of it: Issue 8568:LiveBroadcasts: insert - spaces in part
No matter what I do I'm always getting the same AuthorizationException for the following request on sandbox with valid keys:
REQUEST:
{"CreditCard":
{
"CVV":"123",
"ExpirationMonth":"10",
"ExpirationYear":"2016",
"Number":"4111 1111 1111 1111"
},
"Amount":195.000000,
"OrderId":"bb461ebb-b894-4716-9ea2-7317f9e8c8d9",
"MerchantAccountId":"xxxxx",
"TaxAmount":0,
"Type":{},
"CustomFields":
{
"correlation_id":"bb461ebb-b894-4716-9ea2-7317f9e8c8d9"
},
"Options":
{
"StoreInVault":true,
"SubmitForSettlement":true
},
"CustomerId":"2012f124-2f00-477f-85fb-f6bc3f5fe275"
}
Here's the code that I'm using to create this request:
var request = new TransactionRequest
{
OrderId = message.Id.ToString(),
Amount = message.Amount,
CustomerId = message.CustomerId.ToString(),
MerchantAccountId = message.MerchantAccountId,
Options = new TransactionOptionsRequest
{
SubmitForSettlement = message.SubmitForSettlement
}
};
string expirationMonth = message.ExpirationMonth.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
string expirationYear = message.ExpirationYear.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
request.CreditCard = new TransactionCreditCardRequest
{
Token = message.CreditCardId.ToString(),
CardholderName = message.CardholderName,
Number = message.CardNumber,
ExpirationMonth = expirationMonth,
ExpirationYear = expirationYear,
CVV = message.CVV
};
request.Options.StoreInVault = true;
var result = _gateway.Transaction.Sale(request);
What I'm missing?
I work at Braintree. If you have any other questions, feel free to contact our support team directly.
This error can happen when you're attempting to use a merchant account that your user doesn't have access to. In this case, the merchant account ID you're passing (which you X'd out above) doesn't exist.
Take a look at our support pages for more information on merchant account IDs:
Merchant Account ID
With Braintree, you can have multiple merchant accounts processing via the same single gateway account. You could have multiple locations, multiple businesses, and multiple currencies all setup and processing under one single account. This makes it easy to keep track of all of your processing with unified reporting and access and can even save you money.
You can find the values for all merchant accounts in your gateway account by following these steps:
Log in to the Control Panel
Navigate to Settings -> Processing
Scroll to the bottom of the page to find the section labeled Merchant Accounts
If I only have a single merchant account, do I still need to send this value with API requests?
No, this is an optional value with all API requests that support it. If you only have a single merchant account, there's no need to include this value. If you have more than one merchant account you can specify which merchant account should be used with each API request. If you omit this value, all requests will automatically be routed through your default account.
I use the PayPal Express Checkout SOAP service. For example here's a trimmed down version of the code to redirect the user to PayPal Sandbox when checking out:
var client = new PayPalAPIAAInterfaceClient();
var credentials = new CustomSecurityHeaderType() {
Credentials = new UserIdPasswordType() { ... }
};
var paymentDetails = new PaymentDetailsType() {
OrderTotal = new BasicAmountType() {
Value = string.Format("{0:0.00}", 100m)
}
};
var request = new SetExpressCheckoutReq() {
SetExpressCheckoutRequest = new SetExpressCheckoutRequestType() {
SetExpressCheckoutRequestDetails = new SetExpressCheckoutRequestDetailsType() {
PaymentDetails = new PaymentDetailsType[] { paymentDetails },
CancelURL = "http://www.mysite.com" + Url.Action("Cancelled", "PayPalCheckout"),
ReturnURL = "http://www.mysite.com" + Url.Action("Index", "PayPalCheckout")
},
Version = "60.0"
}
};
var response = client.SetExpressCheckout(ref credentials, request);
return Redirect(string.Format("{0}?cmd=_express-checkout&token={1}", "https://www.sandbox.paypal.com/cgi-bin/webscr", response.Token));
I then handle the data when the user is returned to the ReturnUrl. This was taken from some code I found on another website.
I now need to add a refund facility to my site. I was wondering if anyone else has done this? I've tried searching online but can't seem to find anything that helps. I also tried doing it myself but the API isn't very intuitive.
I'd appreciate the help. Thanks
It would just need to be a RefundTransaction API call that you would need to execute. Are you trying to have your return page issue a refund based on a condition, or are you trying to create a GUI type of interface to allow someone to issue a refund for a transaction? Have you looked at the code samples for this within the SDK's that PayPal offers? You should be able to use this code.