0
votes

When I receive a webhook from Stripe saying that a subscription was created, I'm trying to update my database to give that user access to my subscription service. The webhook comes in saying that a subscription was created, so I take the UserID and use that to lookup the user in my database. Then I update the user record changing SubscriptionStatus to 1 indicating that it is active. When I try to save that information the database, it throws an exception and I receive an error from Stripe saying that the webhook failed with Error 400 (Bad Request). According to the response from Stripe, it says "The request could not be understood by the server due to malformed syntax."

This is the Response I received from Stripe:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 10.0 Detailed Error - 400.0 - Bad Request</title> 
<style type="text/css"> 
<!-- 
body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;} 
code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;} 
.config_source code{font-size:.8em;color:#000000;} 
pre{margin:0;font-size:1.4em;word-wrap:break-word;} 
ul,ol{margin:10px 0 10px 5px;} 
ul.first,ol.first{margin-top:5px;} 
fieldset{padding:0 15px 10px 15px;word-break:break-all;} 
.summary-container fieldset{padding-bottom:5px;margin-top:4px;} 
legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;} 
legend{color:#333333;;margin:4px 0 8px -12px;_margin-top:0px; 
font-weight:bold;font-size:1em;} 
a:link,a:visited{color:#007EFF;font-weight:bold;} 
a:hover{text-decoration:none;} 
h1{font-size:2.4em;margin:0;color:#FFF;} 
h2{font-size:1.7em;margin:0;color:#CC0000;} 
h3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;} 
h4{font-size:1.2em;margin:10px 0 5px 0; 
}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif; 
 color:#FFF;background-color:#5C87B2; 
}#content{margin:0 0 0 2%;position:relative;} 
.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;} 
.content-container p{margin:0 0 10px 0; 
}#details-left{width:35%;float:left;margin-right:2%; 
}#details-right{width:63%;float:left;overflow:hidden; 
}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF; 
 background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal; 
 font-size:1em;color:#FFF;text-align:right; 
}#server_version p{margin:5px 0;} 
table{margin:4px 0 4px 0;width:100%;border:none;} 
td,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:normal;border:none;} 
th{width:30%;text-align:right;padding-right:2%;font-weight:bold;} 
thead th{background-color:#ebebeb;width:25%; 
}#details-right th{width:20%;} 
table tr.alt td,table tr.alt th{} 
.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;} 
.clear{clear:both;} 
.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;} 
--> 
</style> 
 
</head> 
<body> 
<div id="content"> 
<div class="content-container"> 
  <h3>HTTP Error 400.0 - Bad Request</h3> 
  <h4>Bad Request</h4> 
</div> 
<div class="content-container"> 
 <fieldset><h4>Most likely causes:</h4> 
  <ul>  <li></li> </ul> 
 </fieldset> 
</div> 
<div class="content-container"> 
 <fieldset><h4>Things you can try:</h4> 
  <ul>  <li>Check the failed request tracing logs for additional information about this error. For more information, click <a href="http://go.microsoft.com/fwlink/?LinkID=66439">here</a>. </li> </ul> 
 </fieldset> 
</div> 
 
<div class="content-container"> 
 <fieldset><h4>Detailed Error Information:</h4> 
  <div id="details-left"> 
   <table border="0" cellpadding="0" cellspacing="0"> 
    <tr class="alt"><th>Module</th><td>&nbsp;&nbsp;&nbsp;ManagedPipelineHandler</td></tr> 
    <tr><th>Notification</th><td>&nbsp;&nbsp;&nbsp;ExecuteRequestHandler</td></tr> 
    <tr class="alt"><th>Handler</th><td>&nbsp;&nbsp;&nbsp;System.Web.Mvc.MvcHandler</td></tr> 
    <tr><th>Error Code</th><td>&nbsp;&nbsp;&nbsp;0x00000000</td></tr> 
     
   </table> 
  </div> 
  <div id="details-right"> 
   <table border="0" cellpadding="0" cellspacing="0"> 
    <tr class="alt"><th>Requested URL</th><td>&nbsp;&nbsp;&nbsp;https://localhost:44394/Stripe/StripeWebhook</td></tr> 
    <tr><th>Physical Path</th><td>&nbsp;&nbsp;&nbsp;C:\Users\steve\source\repos\Church Musician Administration App\Church Musician Administration App (Updated)\Stripe\StripeWebhook</td></tr> 
    <tr class="alt"><th>Logon Method</th><td>&nbsp;&nbsp;&nbsp;Anonymous</td></tr> 
    <tr><th>Logon User</th><td>&nbsp;&nbsp;&nbsp;Anonymous</td></tr> 
     
   </table> 
   <div class="clear"></div> 
  </div> 
 </fieldset> 
</div> 
 
<div class="content-container"> 
 <fieldset><h4>More Information:</h4> 
  The request could not be understood by the server due to malformed syntax. 
  <p><a href="https://go.microsoft.com/fwlink/?LinkID=62293&amp;IIS70Error=400,0,0x00000000,19042">View more information &raquo;</a></p> 
  <p>Microsoft Knowledge Base Articles:</p> 
 <ul><li></li></ul> 
 
 </fieldset> 
</div> 
</div> 
</body> 
</html>

This is my Webhook Endpoint in my app:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Database_Access;
using Stripe;

namespace Church_Musician_Administration_App__Updated_.Controllers
{

    public class StripeController : Controller
    {
        private churchmusicianEntities db = new churchmusicianEntities();
        [AllowAnonymous]
        [HttpPost]
        public ActionResult StripeWebhook()
        {
            try
            {
                var json = new StreamReader(HttpContext.Request.InputStream).ReadToEnd();

                // validate webhook called by stripe only
                var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], "whsec_jLsw8GxNmEKkcyxhjNeDTpJvI5xhggXu");

                switch (stripeEvent.Type)
                {
                    case "customer.created":
                        var customer = stripeEvent.Data.Object as Customer;
                        // do work

                        break;

                    case "customer.subscription.created":
                        var subscriptionCreated = stripeEvent.Data.Object as Subscription;
                        string subscriptionCustID = subscriptionCreated.CustomerId;
                        var subscriptionCustomers = db.logins.Where(x => x.StripeCustomerID == subscriptionCustID);
                        foreach (var subscriptionCustomer in subscriptionCustomers)
                        {
                            if (subscriptionCreated.Status == "trialing" || subscriptionCreated.Status == "active")
                            {
                                subscriptionCustomer.SubscriptionStatus = 1;
                                db.Entry(subscriptionCustomer).State = EntityState.Modified;
                                db.SaveChanges();
                                
                            }
                        }

                        break;
                    case "customer.subscription.updated":
                    case "customer.subscription.deleted":
                    case "customer.subscription.trial_will_end":
                        var subscription = stripeEvent.Data.Object as Subscription;
                        // do work
                        
                        break;

                    case "invoice.created":
                        var newinvoice = stripeEvent.Data.Object as Invoice;
                        // do work
                        
                        break;

                    case "invoice.upcoming":
                    case "invoice.payment_succeeded":
                        var successPayment = stripeEvent.Data.Object as Invoice;
                        string customerID = successPayment.CustomerId;
                        var custs = db.logins.Where(x => x.StripeCustomerID == customerID);
                        foreach (var cust in custs)
                        {
                            cust.SubscriptionExpiration = successPayment.PeriodEnd.ToString();
                            db.SaveChanges();
                        }
                        
                        break;
                    case "invoice.payment_failed":
                        var changeSubscription = stripeEvent.Data.Object as Invoice;
                        
                        string customerID2 = changeSubscription.CustomerId;
                        if(changeSubscription.Status != "active")
                        {
                            var payingCustomers = db.logins.Where(x => x.StripeCustomerID == customerID2);
                            foreach (var payingCustomer in payingCustomers)
                            {
                                payingCustomer.SubscriptionStatus = 0;
                                db.SaveChanges();
                            }
                        }
                            

                        

                        // do work

                        break;

                    case "coupon.created":
                    case "coupon.updated":
                    case "coupon.deleted":
                        var coupon = stripeEvent.Data.Object as Coupon;
                        // do work

                        break;

                        // DO SAME FOR OTHER EVENTS
                }

                return new HttpStatusCodeResult(HttpStatusCode.OK);
            }
            catch (StripeException ex)
            {
                //_logger.LogError(ex, $"StripWebhook: {ex.Message}");
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            catch (Exception ex)
            {
                //_logger.LogError(ex, $"StripWebhook: {ex.Message}");
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }


        }
    }
}
1

1 Answers

0
votes

The problem is likely due to something going wrong in your database logic, which is causing your server to return the full error to Stripe, which Stripe cannot read. Instead you should return a 200 early to indicate that the webhook event was received, and then handle your business logic: https://stripe.com/docs/webhooks/build#return-a-2xx-status-code-quickly