#Slush - Payments Quickstart
1 messages · Page 1 of 1 (latest)
Hey!
What do you mean the payload isn't working?
Are you familiar with working in .NET 🙂
Only a little bit
The payload itself is working as seen here:
So we know the frontend is working correctly 🙂
When that gets called my breakpoint on the server side does get hit. So we know its connecting with the backend.
But for some reason public ActionResult Create(PaymentIntentCreateRequest request)
"request" doesnt contain the items payload
The guide says it does?
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
The prebuilt Checkout page supports Items
"Custom Payment Flow"
Look at the checkout.js code and Server.cs code
They are both sending and recieving items
Am I wrong?
I'm looking at server.cs
And no, it's not using items
The only options passed in are the amount and the currency and enabling automatic payment methods
Look at checkout.js its sending over items as the payload
Checkout JS doesn't matter here
The payment intent does not support line items and is not created with them
and if you look at CalculateOrderAmount its called from the create function
using "request.Items"
Which is fine but the Payment Intent is not created with items because it does not support line items
All that is doing in this guide is providing a mechanism to calculate the total anount
Yes but how come "request.Items" is null
Do you have a request ID?
That's the Payment Intent ID but that will work
That will work for what?
FOr me to examine what you are talking about
"pi_3Ldd91K8LPxiZPYt13wUYpsm"
But just to be absolutely clear. You are creating a payment intent. Payment intents do not have lines.
This is the data you sent to the API
{
automatic_payment_methods: {
enabled: "True"
},
amount: "1400",
currency: "usd"
}
Okay maybe I can explain to you what I am trying to do and how I can edit this so that it accomplishes what I am attempting to do.
On the frontend I need an ID value passed over to the server to be able to correctly calculate the order amount. (As you know right now its just returning 1400) so when the payment intent service gets called, I also need it to contain the ID value that will be passed in from the frontend.
How would you suggest going about this?
- You can create a Price object in Stripe that defines the amount for something specific. This will return a
price_XXXXID.
But Payment Intents are one of our most simple APIs so they just need amount and currency
All the prices are stored on my own backend and are completely random based on the ID that gets passed in on the front end.
So you would need to get the Price object to determine what the amount should be
Okay, that works too
as long as your integration knows what to provide in the amount parameter
So what part of the code are you talking about editing so I can include this ID?
For example on the .js file I have a constant called "goldenID" how should I be passing that to the server along with the paymentintent.
If you are talking about sending data to the API, the area you want to focus on is the server.cs and especially this code:
var paymentIntent = paymentIntentService.Create(new PaymentIntentCreateOptions
{
Amount = CalculateOrderAmount(request.Items),
Currency = "usd",
AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions
{
Enabled = true,
},
});
You could include the ID in the metadata parameter for the Payment Intent
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
Hmm, okay maybe this makes sense. But still trying to understand fully.
Basically on the frontend .js file I have a "goldenID"
{
Amount = CalculateOrderAmount(request.GOLDENID),
Currency = "usd",
AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions
{
Enabled = true,
},
});```
I basically need that "goldenID" passed into my calculateOrderAmount method in order for me to get the correct price.
How would you suggest getting that value there?
Pass the ID back to your server in the request, then add it to the PaymentIntentCreateOptions metadata parameter
Which is what I think I am trying to do with the request.Items? I was just trying to use that as storage for my ID
How would I alter this: const response = await fetch("api/Stripe/create-payment-intent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ items }), }); to allow me to pass in my custom ID to the request?
đź‘‹ I'm hopping in since @pallid cedar has to head out soon - give me a minute to catch up
Thank you @balmy seal 🙂
Long story short - .NET - Following this guide: https://stripe.com/docs/payments/quickstart - on the frontend I have a custom ID that I need to be able to calculate the order price - How do I get that custom ID over with the create payment intent request
You'd pass that in the body when you call your server endpoint that makes the Payment Intent. See in the code you sent over it has body: JSON.stringify({ items }) - you/d want to change that so it sends over items as well as the customer ID you want
Then how do I access that on the server side?
Because right now I cant even access the items
The example should be showing you how to access items - what do you mean when you say you can't access them? Are you getting an error?
No error, items is just null
Thats the code I have, as you can see items = id xl-tshirt
Just like it is on the example
Thats the payload successfully being sent
What happens when you change your code to body: JSON.stringify ({items: items})
ah actually the request payload does seem to indicate it's correct
On the server side request.Items is null
What happens when you log request on your server-side?
When i debug it and that function gets called
I hover over request and it says "Items null"
Hmmm... Can you change your code to log the raw request body you get back?
Hmm... Can you show me the server-side code you have for your Item and PaymentIntentCreateRequest classes?
I sure can!
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Stripe;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace Tourny.Controllers
{
[Route("api/[controller]/create-payment-intent")]
public class StripeController : Controller
{
// GET: api/<controller>
[HttpPost]
public ActionResult Create(PaymentIntentCreateRequest request)
{
//StripeConfiguration.ApiKey = "sk_test_XXX";
var paymentIntentService = new PaymentIntentService();
var paymentIntent = paymentIntentService.Create(new PaymentIntentCreateOptions
{
Amount = CalculateOrderAmount(request.Items),
Currency = "usd",
AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions
{
Enabled = true,
},
});
return Json(new { clientSecret = paymentIntent.ClientSecret });
}
private int CalculateOrderAmount(Item[] items)
{
// Replace this constant with a calculation of the order's amount
// Calculate the order total on the server to prevent
// people from directly manipulating the amount on the client
return 1400;
}
public class Item
{
[JsonProperty("id")]
public string Id { get; set; }
}
public class PaymentIntentCreateRequest
{
[JsonProperty("items")]
public Item[] Items { get; set; }
}
}
}
Here is my Startup.cs file:
namespace Tourny
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDistributedMemoryCache();
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(60);//You can set Time
});
services.AddRazorPages();
services.AddMvc().AddNewtonsoftJson();
//services.AddControllersWithViews().AddRazorRuntimeCompilation();
//services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
StripeConfiguration.ApiKey = "sk_test_xxxxx";
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSession();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
//endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}```
Hmm... Yeah I don't see anything out of the ordinary with that code so I'm a bit confused about why it's not working
Let me mull this over a bit
Thank you.
This line: CalculateOrderAmount(request.Items),
request.Items should NOT be null right?
correct - if this was all working, the JSON from the request body would be converted into a PaymentIntentCreateRequest and populate items
You're sure it's being sent as a POST request right?
Yeah I think so
How can I check
It does say method: “POST”
On sending the request on the JavaScript side
gotcha
other than changing your code so that we can see the raw request body (I think our webhooks sample code gives an example of how to do this https://stripe.com/docs/webhooks/signatures#verify-official-libraries), the only other thing I can think of is to change up/simplify the request body to just send a string and then use [FromBody] (https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#using-frombody) to make sure the request can pull out that string from the reqeust body. With the intention being to see if something is wrong with how the json is being deserialized
Sorry not exactly sure what you’re asking me to try
Is there anything I can log on the JavaScript side so you can see the raw request?
No, there isn't anything you can change on the Javascript side to see the raw request - you're already looking at it from the screenshot you sent over
The issue is on the server-side, not doing what we expect it to (which is why we need to see the raw body it's getting)
My suggestion was to change the request body and simplify it to just be a string just to see if that works, but really, the best thing would be to get the raw request body
It probably wouldn't - we need to know what the reqeust looks like BEFORE it's been de-serialized. By the time you call request.toString() it's too late.
public ActionResult Create(PaymentIntentCreateRequest request)
{
string output = request.ToString();
//StripeConfiguration.ApiKey = "sk_test_XXX";
var paymentIntentService = new PaymentIntentService();
var paymentIntent = paymentIntentService.Create(new PaymentIntentCreateOptions
{
Amount = CalculateOrderAmount(request.Items),
Currency = "usd",
AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions
{
Enabled = true,
},
});
return Json(new { clientSecret = paymentIntent.ClientSecret });
}```
Take a look at that.
That should turn it into a string the second the request comes in right?
Output contains "Tourny.Controllers.StripeController+PaymentIntentCreateRequest"
Thats it.
Yeah that's because what I mentioned before - that isn't enough to get your the raw request body. At that point the JSON from the request has already been de-serialized
So when do you suggest i grab the request?
I'm suggesting you temporarily change your endpoint to match what we suggest for webhook endpoints (https://stripe.com/docs/webhooks/signatures#verify-official-libraries) because it has a clear example of how to take in the raw request body. From there, you can log the raw request body and confirm it's in the form you expect.
{"items":[{"id":"xl-tshirt"}]}
reading the var json = await new StreamReader....
json contains that ^^
Hi stepping in for karbi as they have to step out
Seeing this is a very long running thread
Can you summarize exactly what you are blocked on this moment?
Long story short - .NET - Following this guide: https://stripe.com/docs/payments/quickstart - Trying to get the items from the frontend to appear on the server side
but the request.Items = null
frontend the payload looks fine:
Karbi suggested for me to use the webhook method to see the request stream from the backend to make sure its working
So I replaced the "Create" method with this:
public async Task<IActionResult> Create()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
try
{
var stripeEvent = EventUtility.ConstructEvent(json,
Request.Headers["Stripe-Signature"], endpointSecret);
// Handle the event
if (stripeEvent.Type == Events.PaymentIntentSucceeded)
{
var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
Console.WriteLine("PaymentIntent was successful!");
}
else if (stripeEvent.Type == Events.PaymentMethodAttached)
{
var paymentMethod = stripeEvent.Data.Object as PaymentMethod;
Console.WriteLine("PaymentMethod was attached to a Customer!");
}
// ... handle other event types
else
{
Console.WriteLine("Unhandled event type: {0}", stripeEvent.Type);
}
return Ok();
}
catch (StripeException)
{
return BadRequest();
}
}```
And grabbed the output of var json
and that read: {"items":[{"id":"xl-tshirt"}]}
So we know that the request is getting sent successfully to the backend as well
But there is just something wrong with this method:
public ActionResult Create(PaymentIntentCreateRequest request)
{
//StripeConfiguration.ApiKey = "sk_test_XXX";
var paymentIntentService = new PaymentIntentService();
var paymentIntent = paymentIntentService.Create(new PaymentIntentCreateOptions
{
Amount = CalculateOrderAmount(request.Items),
Currency = "usd",
AutomaticPaymentMethods = new PaymentIntentAutomaticPaymentMethodsOptions
{
Enabled = true,
},
});
return Json(new { clientSecret = paymentIntent.ClientSecret });
}```
Where for some reason the request.Items is returning null when we know it shouldnt be.
Wait so to clarify that second code block you sent there is your original code?
Yes
I replaced that with the top code block just so I can see the request body before it was serialized
Gotcha. Can you try adding [FromBody into that method? So, it would be like: public ActionResult Create([FromBody] PaymentIntentCreateRequest request)
Yes let me give it a try
holy fucking shit
you did it
I have been working on this problem
for like 4 days
Haha. Happy to help