asp.net core 3.1在条带HttpPost("webhook")中获取当前身份用户返回NULL [英] asp.net core 3.1 getting current identity user within stripe HttpPost("webhook") returns NULL

查看:80
本文介绍了asp.net core 3.1在条带HttpPost("webhook")中获取当前身份用户返回NULL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已根据Stripe的示例

I've integrated stripe checkout payments in my website according to stripe's example

一切正常.我可以验证webhooks是否可以与Stripe CLI一起使用,并且还可以使用ngrok,通过隧道传输 localhost .

Everything works fine. I could verify webhooks are working with stripe CLI and also using ngrok, tunneling my localhost.

现在,我已经开始实现与身份数据库的交互.我想在Webhook触发 checkout.session.completed 后将条纹 session.CustomerId 存储在那里.

Now I've started implementing interaction with the identity database. I wanto to store there the stripe session.CustomerId after the webhook has fired checkout.session.completed.

为此,我需要访问我的身份数据库.

For that I need to access my Identity database.

我的代码是:

[HttpPost("webhook")]
    public async Task<IActionResult> Webhook()
    {
        // GET logged current user
        var currentUser = await _userManager.GetUserAsync(User);

        var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
        Event stripeEvent;
        try
        {
            stripeEvent = EventUtility.ConstructEvent(
                json,
                Request.Headers["Stripe-Signature"],
                this.options.Value.WebhookSecret
            );
            Console.WriteLine($"Webhook notification with type: {stripeEvent.Type} found for {stripeEvent.Id}");
        }
        catch (Exception e)
        {
            Console.WriteLine($"Something failed {e}");
            return BadRequest();
        }



        // Handle the event
        if (stripeEvent.Type == Events.PaymentIntentSucceeded) //Success
        {
            var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
            var subscriptionItem = stripeEvent.Data.Object as Stripe.Subscription;
            var subscriptionInvoice = stripeEvent.Data.Object as Invoice;
            var paidTimestamp = paymentIntent.Created;
            var paidValidityTimestamp = subscriptionItem.CurrentPeriodEnd;
            Console.WriteLine("*********************");
            Console.WriteLine(".....................");
            Console.WriteLine("Payment Intent Succeeded");
            // Then define and call a method to handle the successful payment intent.
            // handlePaymentIntentSucceeded(paymentIntent);

            paymentIntent.ReceiptEmail = currentUser.Email;
            paymentIntent.Shipping.Address.City = currentUser.City;
            paymentIntent.Shipping.Address.PostalCode = currentUser.PostalCode.ToString();
            paymentIntent.Shipping.Address.Line1 = currentUser.Street + " " + currentUser.CivicNumber;

            currentUser.hasPaidQuote = true;
            currentUser.paidOnDate = paidTimestamp;
            currentUser.paidValidity = paidValidityTimestamp;
            currentUser.SubscriptionStatus = paymentIntent.Status;
            currentUser.Status = LoginUserStatus.Approved;
            await _userManager.UpdateAsync(currentUser);

            Console.WriteLine("has paid? " + currentUser.hasPaidQuote);
            Console.WriteLine("paid on date: " + currentUser.paidOnDate.Date.ToString());
            Console.WriteLine("payment validity: " + currentUser.paidValidity.Date.ToString());
            Console.WriteLine("subscription status: " + currentUser.SubscriptionStatus);
            Console.WriteLine("user status: " + currentUser.Status.ToString());
            Console.WriteLine("customer ID: " + paymentIntent.Customer.Id);
            Console.WriteLine("payment intent status: " + paymentIntent.Status);
            Console.WriteLine("invoice status from paymentIntent: " + paymentIntent.Invoice.Status);
            Console.WriteLine("invoice status from subscriptionItem: " + subscriptionItem.LatestInvoice.Status);
            Console.WriteLine("subscription status: " + subscriptionItem.Status);
            Console.WriteLine("invoice status: " + subscriptionInvoice.Paid.ToString());

            Console.WriteLine(".....................");
            Console.WriteLine("*********************");

        }
        else if (stripeEvent.Type == Events.PaymentIntentPaymentFailed) //Fails due to card error
        {
            var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
            Console.WriteLine("*********************");
            Console.WriteLine(".....................");
            Console.WriteLine("Payment Intent requires payment method. Payment failed due to card error");
            // Then define and call a method to handle the successful attachment of a PaymentMethod.
            // handlePaymentMethodAttached(paymentMethod);

            Console.WriteLine("payment intent status: " + paymentIntent.Status);
            Console.WriteLine("invoice status: " + paymentIntent.Invoice.Status);
            Console.WriteLine(".....................");
            Console.WriteLine("*********************");
        }
        else if (stripeEvent.Type == Events.PaymentIntentRequiresAction) //Fails due to authentication
        {
            var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
            Console.WriteLine("*********************");
            Console.WriteLine(".....................");
            Console.WriteLine("Payment Intent requires actions");
            // Then define and call a method to handle the successful attachment of a PaymentMethod.
            // handlePaymentMethodAttached(paymentMethod);

            Console.WriteLine("payment intent status: " + paymentIntent.Status);
            Console.WriteLine("invoice status: " + paymentIntent.Invoice.Status);
            Console.WriteLine(".....................");
            Console.WriteLine("*********************");
        }
        else if (stripeEvent.Type == Events.PaymentMethodAttached)
        {
            var paymentMethod = stripeEvent.Data.Object as PaymentMethod;
            Console.WriteLine("Payment Method Attached");
            // Then define and call a method to handle the successful attachment of a PaymentMethod.
            // handlePaymentMethodAttached(paymentMethod);
        }
        // ... handle other event types




        if (stripeEvent.Type == "checkout.session.completed")
        {
            var session = stripeEvent.Data.Object as Stripe.Checkout.Session;
            ViewBag.eventID = stripeEvent.Id;

            Console.WriteLine("*********************");
            Console.WriteLine(".....................");
            Console.WriteLine("checkout session completed");
            Console.WriteLine($"Session ID: {session.Id}");

            // Take some action based on session.
            currentUser.StripeCustomerId = session.CustomerId;
            await _userManager.UpdateAsync(currentUser);

            Console.WriteLine("Ssession.CustomerId has been retrieved from session and will be stored into database: " + session.CustomerId);
            Console.WriteLine("StripeCustomerId has been stored into database: " + currentUser.StripeCustomerId);
            Console.WriteLine(".....................");
            Console.WriteLine("*********************");
        }

        return Ok();
    }

我的问题是 var currentUser = await _userManager.GetUserAsync(User); 返回NULL.

My problem is that var currentUser = await _userManager.GetUserAsync(User); is returning NULL.

如果我在调试模式下运行代码,则制动点上会出现一个带感叹号的蓝色圆圈.鼠标悬停在那儿,我得到通知:自上一步以来,进程或线程已更改".使用stripe的checkout API会将我重定向到某个地方,丢失了会话的信息,因此也丢失了到我的Identity数据库的任何链接.

If I run the code in debugging mode a blue circle with an exclamation mark is showing up on my braking point. Hovering the mouse There I get the notification: "The process or thread has changes since last step". Using checkout API of stripe redirects me somewhere, losing the information of my session, hence losing also any link to my Identity database.

如何获取变量 currentUser 包含已登录的用户?

How can I obtain that my variable currentUser contains the logged-in user?

我的 PaymentsController 如下:

public class PaymentsController : Controller
{
    public readonly IOptions<StripeOptions> options;
    private readonly IStripeClient client;
    private UserManager<VivaceApplicationUser> _userManager;

    private readonly IConfiguration Configuration;


    public IActionResult Index()
    {
        return View();
    }

    public IActionResult canceled()
    {
        return View();
    }

    public IActionResult success()
    {
        return View();
    }

    public PaymentsController(IOptions<StripeOptions> options,
        IConfiguration configuration,
        UserManager<VivaceApplicationUser> userManager)
    {
        this.options = options;
        this.client = new StripeClient(this.options.Value.SecretKey);
        _userManager = userManager;
        Configuration = configuration;
    } .......

}

我的 [HttpGet(" checkout-session")]

[HttpGet("checkout-session")]
    public async Task<IActionResult> CheckoutSession(string sessionId)
    {
        var currentUser = await _userManager.GetUserAsync(HttpContext.User);
        var service = new SessionService(this.client);
        var session = await service.GetAsync(sessionId);

        // Retrieve prices of product
        var product = new PriceService();
        var productBasic = product.Get(Configuration.GetSection("Stripe")["BASIC_PRICE_ID"]);
        var productMedium = product.Get(Configuration.GetSection("Stripe")["MEDIUM_PRICE_ID"]);
        var productPremium = product.Get(Configuration.GetSection("Stripe")["PREMIUM_PRICE_ID"]);

        int quoteBasic = (int)productBasic.UnitAmount;
        int quoteMedium = (int)productMedium.UnitAmount;
        int quotePremium = (int)productPremium.UnitAmount;

        int selectedPlan = (int)session.AmountTotal;
       
        if (!string.IsNullOrEmpty(session.Id))
        {
            // storing stripe customerId into the User database
            // this will be done if checkout.session.completed
            //currentUser.StripeCustomerId = session.CustomerId;
            
            if (selectedPlan == quoteBasic)
            {
                currentUser.Subscription = Models.VivaceUsers.Subscription.Basic;
            }
            else if (selectedPlan == quoteMedium)
            {
                currentUser.Subscription = Models.VivaceUsers.Subscription.Medium;
            }
            else if (selectedPlan == quotePremium)
            {
                currentUser.Subscription = Models.VivaceUsers.Subscription.Premium;
            }

            await _userManager.UpdateAsync(currentUser);
        }

        return Ok(session);
    }

在结帐会话"中,我可以简单地使用以下方法检索currentUser:

In the "checkout-session" I can retrieve the currentUser without any problem using simply:

var currentUser = await _userManager.GetUserAsync(HttpContext.User);

如何在 webhook 方法中启动并运行它?

How canI get it up and running in the webhook method?

推荐答案

问题是,当您从Stripe收到POST请求时,没有用户会话.身份验证会话可能由Cookie管理,并由客户的浏览器保留.当直接从Stripe收到POST请求时,该请求将不包含与您已通过身份验证的用户相关的任何cookie.

The issue is that there is no user session when you receive the POST request from Stripe. The authentication session is likely managed with cookies and persisted by your customer's browser. When receiving the POST request directly from Stripe, the request will not include any cookies related to your authenticated user.

您需要使用另一种方式将Checkout会话与数据库中的用户相关联,而不是使用以下内容来查找当前用户.

Instead of using the following to lookup the current user, you'll need another way to associate the Checkout Session to a user in your database.

var currentUser = await _userManager.GetUserAsync(HttpContext.User);

有两种解决方法.

  1. 将用户的ID存储在Checkout会话的元数据中.

  1. Store the ID of the user in the metadata for the Checkout Session.

将Checkout会话的ID存储在数据库中,并返回到当前用户.

Store the ID of the Checkout Session in your database with some relation back to the current user.

许多用户在选项1上都取得了成功,创建Checkout会话的外观可能大致类似于以下内容:

Many users have success with option 1 and it might look something roughly like the following for creating the Checkout Session:

var currentUser = await _userManager.GetUserAsync(HttpContext.User);
var options = new SessionCreateOptions
{
    SuccessUrl = "https://example.com/success",
    Metadata = new Dictionary<string, string>
    {
        {"UserId", currentUser.ID},
    }
    //...
};

然后稍后从webhook处理程序中查找用户时:

Then later when looking up the User from the webhook handler:

var currentUser = await _userManager.FindByIdAsync(checkoutSession.Metadata.UserId);

这篇关于asp.net core 3.1在条带HttpPost("webhook")中获取当前身份用户返回NULL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆