Using Stripe to process payments on server - c#

I have a vue.js project as a UI which sends requests to my .NET Core API app to process typical CRUD requests.
I have been following the documentation on https://stripe.com/docs/payments/accept-a-payment-synchronously
I've set up the payment method creation on the vue.js project which creates an identifier for the users card details, which is then sent to the API to be processed/completed:
[HttpPost("buy")]
public async Task<IActionResult> Buy(PaymentRequest request)
{
StripeConfiguration.ApiKey = _stripeSettings.Key;
var options = new PaymentIntentCreateOptions
{
PaymentMethod = request.paymentMethod,
Amount = 300,
Currency = "gbp",
ConfirmationMethod = "manual",
Confirm = true
};
var intent = await _paymentIntentService.CreateAsync(options);
return Ok(intent.ClientSecret);
}
I am then able to view this successful transaction on my Stripe dashboard.
I can't find anything useful online, but is there a way to process all of this in the backend without my frontend knowing anything about stripe? The reason I ask, is because I would like some sort of seperation of concerns as if I was to change the integration for accepting payments in future I wouldn't need to update the frontend.

Related

Get Stripe to charge existing card on subscription change

I'm offering my customers monthly subscriptions. When they sign up to a paid subscription plan for the first time they are shown a PaymentElement to capture their credit card details. Once this is complete the customer has a new payment method stored on Stripe. This works fine.
However, when the customer wants to upgrade to a higher subscription plan, Stripe creates a prorated payment on the customer, but for some reason, doesn't actually charge the customer's existing payment method. The payment on Stripe is sitting at Incomplete with the message The customer hasn't attempted to pay this invoice yet.
My question is, why doesn't Stripe just charge the customer using the already stored payment method? Why is it waiting for the customer to pay?
This is how I'm initiating the upgrade (This is also the same code I use to move to the customer to a paid subscription, capturing their card details):
var service = new SubscriptionService();
var subscription = service.Get(subscriptionId);
var paymentSettings = new SubscriptionPaymentSettingsOptions
{
SaveDefaultPaymentMethod = "on_subscription"
};
var options = new SubscriptionUpdateOptions
{
CancelAtPeriodEnd = false,
ProrationBehavior = "always_invoice",
Items = new List<SubscriptionItemOptions>
{
new SubscriptionItemOptions
{
Id = subscription.Items.Data[0].Id,
Price = planPriceCombo.Item2
}
},
PaymentSettings = paymentSettings,
PaymentBehavior = "default_incomplete"
};
options.AddExpand("latest_invoice.payment_intent");
var updatedSubscription = service.Update(subscriptionId, options);
Have I configured this subscription update incorrectly? How would I go about getting Stripe to just automatically charge the payment method already attached to the customer without waiting?
Your code is passing PaymentBehavior = "default_incomplete" which indicates to Stripe that you don't want them to attempt to pay the Invoice by confirming its underlying PaymentIntent. If you look at updatedSubscription.LatestInvoice.PaymentIntent.Status it likely is in requires_confirmation to reflect this. In that case, you would use the PaymentIntent's ClientSecret value to confirm the PaymentIntent client-side, for example in case 3DS is needed.
Since you want Stripe to automatically attempt payment of the Invoice on creation, what you need to do is switch to PaymentBehavior = "allow_incomplete" instead. This means Stripe will attempt a payment synchronously but if it fails or requires a customer action (such as doing 3DS) it won't error and still go through with the update. This will then let you attempt to confirm the PaymentIntent client-side if needed.

GlobalPay challengeRequestIndicator not showing on Hpp Response

I have a web app that processes payments via globalpay using their HPP solution. It works and is live at the moment but I have tried to implement their save card functionality.
The issue is that with some banks we get and error Invalid Transaction and been advised that it is due to the challenge request indicator not being passed. I have tried adding this in using the HostedPaymentData entity but does not show in the hpp response. there is documentation on builders with the challenege request indicator as well below but not sure how to implement this with my .net request.
They provided me some docs on builders below that has withChallengeRequestIndicator but not sure how to add this to my .net request.
https://github.com/globalpayments/dotnet-sdk/blob/0916facfc690aa4379bbe60d80f08eb40b0e4da2/src/GlobalPayments.Api/Builders/Secure3dBuilder.cs#L163
hostedPaymentData = new HostedPaymentData
{
OfferToSaveCard = true, // display the save card tick box
CustomerExists = true, // new customer
CustomerKey = custKey,
CustomerEmail = userCustomerEmail,
CustomerPhoneMobile = userCustomerPhoneMobile,
AddressesMatch = false,
ChallengeRequestIndicator = ChallengeRequestIndicator.NO_PREFERENCE,
};
Most payments will still process ok but some banks seem to throw and Invalid transaction 101 Error, Global pay advised its to do with this challenge request not being present in the response.
Any help

Amazon Pay SDK CompleteCheckoutSession Error

I'm integrating Amazon pay with my website, using the v2 c# SDK from amazon (MVC App), in the sandbox. Setup is all good, I created the keys, return urls, etc.
In my checkout process, I create the CheckoutSession, which is successful. I click on the Amazon Pay button, log in using my test buyer account, and "pay" for the item using their valid test credit card.
Amazon redirects to my return URL with the checkout session id as expected.
When I try to CompleteCheckoutSession, the result is an error back from the Amazon API
error: InvalidCheckoutSessionStatus
message: You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed
I put in a test line of code to retrieve the CheckoutSession to look at it before I try to complete it, and it shows that the current status is "Open", which is the correct status when trying to complete it, so I'm at a loss at why the checkout session status is invalid.
EDIT:
Note I'm using this flow for my transaction, so there is no "review" of the transaction. Buyer chooses his items on my site.
https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-apb-checkout/additional-payment-button-overview.html
Also note, I'm creating the payload dynamically according to this:
https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-checkout/amazon-pay-script.html#render-button-using-a-checkout-session-object
So when the amazonpay button is clicked, it calls a method on my site which builds the payload which begins the CheckoutSession. I then have the Amazon Session ID in my cache which I save it, and save the total. The payload is returned to the Amazon pay script which then takes me to the Amazon Site. I choose the payment type and click "continue to checkout", which sends me back to my site with the SessionId to do the "complete" step.
My request to CompleteCheckoutSession(sessionId)
{"chargeAmount":{"amount":99,"currencyCode":"USD"}}
result.RawResponse from the client.CompleteCheckoutSession(sessionId) method:
{
"reasonCode":"InvalidCheckoutSessionStatus",
"message":"You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed"
}
EDIT SAMPLE CODE:
I created a brand new test MVC app with basic functionality:
public ActionResult Index()
{
var client = InitiateClient(); //hidden for security
// prepare the request
var request = new CreateCheckoutSessionRequest
(
checkoutReviewReturnUrl: "http://localhost:44300/home/completecheckout",
storeId: "amzn1.application-oa2-client.mystoreid"
);
request.PaymentDetails.PaymentIntent = Amazon.Pay.API.WebStore.Types.PaymentIntent.AuthorizeWithCapture;
request.PaymentDetails.ChargeAmount.Amount = 99;
request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
// generate the signature and payload string that is passed back to the frontend
ViewBag.Signature = client.GenerateButtonSignature(request);
ViewBag.Payload = request.ToJson();
return View();
}
In the Index.cshtml file:
<div id="AmazonPayButton"></div>
<script src="https://static-na.payments-amazon.com/checkout.js"></script>
<script type="text/javascript" charset="utf-8">
amazon.Pay.renderButton('#AmazonPayButton', {
merchantId: 'mymerchantid',
ledgerCurrency: 'USD',
sandbox: true,
checkoutLanguage: 'en_US',
productType: 'PayOnly',
placement: 'Checkout',
buttonColor: 'Gold',
createCheckoutSessionConfig: {
payloadJSON: '#Html.Raw(ViewBag.Payload)', // string generated in step 2
signature: '#Html.Raw(ViewBag.Signature)', // signature generated in step 3
publicKeyId: 'AGPTYXGL5VH6PSYLJUSHTKW6'
}
});
</script>
And finally, the completecheckout code, which is unsuccessful:
public ActionResult CompleteCheckout(string amazonCheckoutSessionId)
{
var client = InitiateClient(); //hidden for security
var request = new CompleteCheckoutSessionRequest(99.00M, Currency.USD);
// send the request
var result = client.CompleteCheckoutSession(amazonCheckoutSessionId, request);
// check if API call was successful
if (!result.Success)
{
throw new Exception("API Call unsuccessful");
}
return View();
}
NOTE: certain keys obfuscated, but actual keys are in the sample code.
Manually creating the CheckoutSession doesn't work with the "no review page" flow that you are following. If you want to integrate this flow, you'll have to let Amazon Pay create the CheckoutSession for you using the createCheckoutSessionConfig parameter in the button code, see here: https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-apb-checkout/add-the-amazon-pay-button.html#4-render-the-button
EDIT:
The code sample below shows how to construct the payload and signature for the 'no order review' payment flow. Please note the CheckoutMode parameter, that instructs Amazon Pay to immediately process the payment, and the checkoutResultReturnUrl, that defines the URL where the buyer is sent to for completing the checkout. When the user arrives at that URL, you will have to call Complete CheckoutSession, which should now succeed.
public ActionResult Index()
{
var client = InitiateClient(); //hidden for security
// prepare the request
var request = new CreateCheckoutSessionRequest
(
checkoutReviewReturnUrl: null,
storeId: "amzn1.application-oa2-client.mystoreid"
);
// instructs Amazon Pay to immediately process the payment
request.WebCheckoutDetails.CheckoutMode = CheckoutMode.ProcessOrder;
request.WebCheckoutDetails.CheckoutResultReturnUrl = "http://localhost:44300/home/completecheckout";
// payment details
request.PaymentDetails.PaymentIntent = Amazon.Pay.API.WebStore.Types.PaymentIntent.AuthorizeWithCapture;
request.PaymentDetails.ChargeAmount.Amount = 99;
request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
// generate the signature and payload string that is passed back to the frontend
ViewBag.Signature = client.GenerateButtonSignature(request);
ViewBag.Payload = request.ToJson();
return View();
}
I had a similar issue and it takes me a very long time to figure it out. This is what happened to me.
I was not intent to create production code. I just want to create a test page with only Amazon Pay button. I used several node js script to getCheckoutSession/updateCheckoutSession. However, when I tried completeCheckoutSession, I got "InvalidCheckoutSessionStatus".
In the end, I found, after I setup all the required parameter and remove all constraint, the updateCheckoutSession gives me a "amazonPayRedirectUrl" in API return. That URL is really important. You need to go that URL in order to complete the review...
This is from Amazon official website:
"Once there are no constraints, the response will return a unique
amazonPayRedirectUrl. Redirect the buyer to that URL to complete
checkout."
The other way to solve this issue is mentioned by "Daniel Lemke". In that way, shoppers don't need to confirm the session. However, you need to make sure to provide all useful information in webCheckoutDetails (paymentDetails, addressDetails). If you only give checkoutMode, you will get "MissingParameterValue".
This is from Amazon official website:
"paymentDetails is required when using 'ProcessOrder'. addressDetails
is also required if you use 'ProcessOrder' with productType set to
'PayAndShip'"

Query Twilio debugger logs via REST Api

We are having problems with numerous incoming calls failing in our app. I'm working on creating a "Failed Calls" report for our app but I'm not getting the data that I want.
Currently (I'm using version 3.4.1.0 of the Twilio Rest Api for C#) I'm trying to call the TwilioRestClient.ListCalls method, but it's only returning calls that have had a CallResource object created. That's not what I want.
On the Twilio site, under the debugger/overview page, I see numerous errors for incoming calls, but none of them show up in the *.ListCalls method call.
So, how can I query the debugger logs via REST api so I can display my own report in our app?
Here's an example of what I'm doing currently...
var failedCallsRequest = new CallListRequest
{
Status = "failed",
StartTime = from,
StartTimeComparison = ComparisonType.GreaterThanOrEqualTo,
Count = _pageCount
};
var failedList = _client.ListCalls(failedCallsRequest).Calls.Where(c => c.Direction == "inbound").ToList();

Simple image file POST to Web Api

I have been reading around building an action method in my Web Api to allow the posting of images, which would be saved on the server and related to a Contact object via a field imageURL.
I have found a few examples online of how to set this up, and am wanting to test them. I currently have:
public Task<HttpResponseMessage> PostFile()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/Content/images");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
string file1 = provider.BodyPartFileNames.First().Value;
//Use file name to update ImageURL of contact
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
from How To Accept a File POST, and whilst I understand the flow I want to test it properly.
What would an example of a client call be to this API method, where I can include the contactid as a header?
I need to understand this properly to test this code sample so I can start understanding this more, but I'm really new to this area. I have kind of a chicken and egg scenario where I understand little about posting and receiving images and need a start point.
Thanks.
You can use a small MVC or ASP application (you only need one page) where you can use some JS plugins to post files to your controller.
Plugins: jQueryFileUpload or DropzoneJS. The last one is more easy to integrate. Both plugins has support for sending additional data.

Categories

Resources