I'm following Stripe's online docs to create an embedded payment form for connected accounts.
My test API key is set correctly in Program.cs
.
This endpoint in my controller is mostly straight from Stripe's API guide, and it happily returns the client secret:
[HttpGet]
[AllowAnonymous]
public ContentResult StripeCheckoutSession()
{
var options = new Stripe.Checkout.SessionCreateOptions
{
LineItems = new List<Stripe.Checkout.SessionLineItemOptions>
{
new Stripe.Checkout.SessionLineItemOptions
{
PriceData = new Stripe.Checkout.SessionLineItemPriceDataOptions
{
Currency = "aud",
ProductData = new Stripe.Checkout.SessionLineItemPriceDataProductDataOptions
{
Name = "T-shirt",
},
UnitAmount = 1000,
},
Quantity = 1,
},
},
PaymentIntentData = new Stripe.Checkout.SessionPaymentIntentDataOptions
{
ApplicationFeeAmount = 123,
},
Mode = "payment",
UiMode = "embedded",
ReturnUrl = "abc={CHECKOUT_SESSION_ID}"
};
var requestOptions = new RequestOptions
{
StripeAccount = "acct_12345...";
};
var service = new SessionService();
Session session = service.Create(options, requestOptions);
return Content(JsonConvert.SerializeObject(session.ClientSecret));
}
In the head of my .cshtml
view, I have added this script element:
<script src="https://js.stripe.com/v3/"></script>
In the body I have this markup:
<div id="checkout">
</div>
Also in the head I have this javascript copied from Stripe's API guide.
<script type="text/javascript">
// initialize Stripe.js
const stripe = Stripe('pk_test_blah...', {
stripeAccount: {{CONNECTED_ACCOUNT_ID}},
});
initialize();
// fetch checkout session and retrieve client secret
async function initialize() {
const fetchClientSecret = async () => {
const response = await fetch("/StripeCheckoutSession", {
method: "POST",
});
const { clientSecret } = await response.json();
return clientSecret;
};
// initialize checkout
const checkout = await stripe.initEmbeddedCheckout({
fetchClientSecret,
});
// mount checkout
checkout.mount('#checkout');
}
</script>
Note, I have replaced {{CONNECTED_ACCOUNT_ID}}
with a reference to the actual value from the view model as follows:
'@Model.StripeAccountID'
When I run the project, nothing happens - a blank white screen. I half expected this because I don't see how initialize()
gets called. And I see it never executes.
So I wrote the js how I think it should work as follows:
<script type="text/javascript">
// initialize Stripe.js
const stripe = Stripe('pk_test_blah...', {
stripeAccount: '@Model.StripeAccountID',
});
$(document).ready(function () {
initialize();
})
// fetch checkout session and retrieve the client secret
async function initialize() {
try {
alert('Start');
const fetchClientSecret = async () => {
const response = await fetch("StripeCheckoutSession", {
method: "GET",
});
const { clientSecret } = await response.json();
return clientSecret;
};
// initialize checkout
alert('Init checkout');
const checkout = await stripe.initEmbeddedCheckout({
fetchClientSecret,
});
// mount checkout
alert('Finish mount');
checkout.mount('#checkout');
}
catch (err) {
alert(err);
}
}
</script>
I see alert('Start')
and then alert('Finish mount')
, but not alert('Init checkout')
.
I also tried using a separate function using an AJAX call to get the client secret. The function itself worked but then initialize checkout failed.
What am I doing wrong? Can anyone help me with this?
Here is the working sample for you, you can check it first. And I am using IActionResult, not ContentResult in this sample code.
@model Demo.StripeSettings
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Stripe Embedded Checkout</title>
<script src="https://js.stripe.com/v3/"></script>
</head>
<body>
<div id="checkout"></div>
<script type="text/javascript">
const stripe = Stripe('@Model.PublishableKey', {
stripeAccount: 'acct_1Q***XAU',
});
document.addEventListener("DOMContentLoaded", function () {
initialize();
});
async function initialize() {
try {
const response = await fetch('/Payment/CreateCheckoutSession', {
method: 'GET',
});
const data = await response.json();
const clientSecret = data.clientSecret;
const checkout = await stripe.initEmbeddedCheckout({
clientSecret: clientSecret,
});
checkout.mount('#checkout');
} catch (error) {
console.error('Error:', error);
}
}
</script>
</body>
</html>
Controller
using Demo.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Stripe.Checkout;
using Stripe;
using System.Diagnostics;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace Demo.Controllers
{
public class PaymentController : Controller
{
private readonly ILogger<PaymentController> _logger;
public PaymentController(ILogger<PaymentController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
var stripeSettings = new StripeSettings
{
PublishableKey = "pk_test_51QHc8D2LtiFaqkvHQ4VaoOG******NBOZuXO4WGqL00Mx3gYALt"
};
return View(stripeSettings);
}
[HttpGet]
[AllowAnonymous]
public IActionResult CreateCheckoutSession()
{
var options = new Stripe.Checkout.SessionCreateOptions
{
LineItems = new List<Stripe.Checkout.SessionLineItemOptions>
{
new Stripe.Checkout.SessionLineItemOptions
{
PriceData = new Stripe.Checkout.SessionLineItemPriceDataOptions
{
Currency = "aud",
ProductData = new Stripe.Checkout.SessionLineItemPriceDataProductDataOptions
{
Name = "T-shirt",
},
UnitAmount = 1000,
},
Quantity = 1,
},
},
PaymentIntentData = new Stripe.Checkout.SessionPaymentIntentDataOptions
{
ApplicationFeeAmount = 123,
},
Mode = "payment",
UiMode = "embedded",
ReturnUrl = "https://your-domain.com/return?session_id=cs_test_a1b2c3d4e5f6g7h8i9j0k"
};
var requestOptions = new RequestOptions
{
StripeAccount = "acct_1Q***XAU",
};
var service = new SessionService();
Session session = service.Create(options, requestOptions);
return Json(new { clientSecret = session.ClientSecret });
}
}
}
StripeSettings
namespace Demo
{
public class StripeSettings
{
public string? SecretKey { get; set; }
public string? PublishableKey { get; set; }
}
}