Search code examples
c#asp.net-core-mvcstripe-paymentsasp.net-core-8

ASP.NET Core 8 MVC project - embedded Stripe payment not working


I'm following Stripe's online docs to create an embedded payment form for connected accounts.

https://docs.stripe.com/connect/direct-charges?platform=web&ui=embedded-form&client=html#mount-checkout

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?


Solution

  • Here is the working sample for you, you can check it first. And I am using IActionResult, not ContentResult in this sample code.

    enter image description here

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