如何在 C# WCF 中接收 Stripe api webhook

How to receive Stripe api webhook in C# WCF

我正在尝试接收来自 Stripe 结帐 post 请求的 Webhook。但 Stripe 文档仅显示 ASP.net 的示例代码,而我使用的是 WCF。 https://stripe.com/docs/payments/checkout/fulfillment#webhooks

这里是 Stripe checkout 的示例请求。

{
  "created": 1326853478,
  "livemode": false,
  "id": "evt_00000000000000",
  "type": "checkout.session.completed",
  "object": "event",
  "request": null,
  "pending_webhooks": 1,
  "api_version": "2019-05-16",
  "data": {
    "object": {
      "id": "cs_00000000000000",
      "object": "checkout.session",
      "billing_address_collection": null,
      "cancel_url": "https://example.com/cancel",
      "client_reference_id": null,
      "customer": null,
      "customer_email": null,
      "display_items": [
        {
          "amount": 1500,
          "currency": "usd",
          "custom": {
            "description": "Comfortable cotton t-shirt",
            "images": null,
            "name": "T-shirt"
          },
          "quantity": 2,
          "type": "custom"
        }
      ],
      "livemode": false,
      "locale": null,
      "payment_intent": "pi_00000000000000",
      "payment_method_types": [
        "card"
      ],
      "submit_type": null,
      "subscription": null,
      "success_url": "https://example.com/success"
    }
  }
}

起初我尝试使用Stripe.Event class作为参数。

[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
void FromStripeCheckoutWrapped(Event data);

但是所有数据都是空值。 所以,我尝试根据 json 要求使用自己的模型 classes。

    public partial class Temperatures
    {
        [DataMember]
        public long Created { get; set; }
        [DataMember]
        public bool Livemode { get; set; }
        [DataMember]
        public string Id { get; set; }
        [DataMember]
        public string Type { get; set; }
        [DataMember]
        public string Object { get; set; }
        [DataMember]
        public object Request { get; set; }
        [DataMember]
        public long PendingWebhooks { get; set; }
        [DataMember]
        public string ApiVersion { get; set; }
        [DataMember]
        public Data Data { get; set; }
    } ...

但是其中 none 收到了正确的数据。

我最后尝试使用 String 参数接收,就像我在 Whosebug 中发现的问题,但在 Java 中。 How to Receive Webhook from Stripe in Java 他说他可以用这种方式打印数据。但我做不到。

我成功的唯一方法是输入 json 请求中所写的每个参数。我也尝试 WrappedBare 作为 WebMessageBodyStyle

[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
void FromStripeCheckoutWrapped(long created, bool livemode, string id ...);

然后我可以正常获取数据..但是做上面的一切都太长了。 如何以有效且正确的方式接收 POST 请求? 请参考 Java 和 Stripe webhook 文档中的问题。
我只想将 POST 请求作为字符串对象读取,而不考虑请求结构,以便我可以解析它。

当我使用以下 DataContract 类:

时,反序列化该示例负载没有问题
[DataContract]
public class Base 
{
   [DataMember(Name="livemode")]
   public bool Livemode {get;set;}
   [DataMember(Name="object")]
   public string @Object {get;set;}
   [DataMember(Name="id")]
   public string Id {get;set;}
}

[DataContract]
public class Event:Base
{
   [DataMember(Name="created")]
   public long Created {get;set;}   
   [DataMember(Name="type")]
   public string Type {get;set;}
   [DataMember(Name="data")]
   public EventData Data {get;set;}
}

[DataContract]
public class EventData
{
   [DataMember(Name="object")]
   public EventObject EventObject {get;set;}
}

[DataContract]
public class EventObject:Base
{
   [DataMember(Name="display_items")]
   public Item[] DisplayItems {get;set;}
}

[DataContract]
public class Item
{
   [DataMember(Name="amount")]
   public double Amount {get;set;}
   [DataMember(Name="currency")]
   public string Currency {get;set;}
}

我在此服务合同中使用该 DataContract:

[ServiceContract]
interface IContract 
{
   [OperationContract]
   [WebInvoke(
        Method = "POST",
        BodyStyle = WebMessageBodyStyle.Bare,
        ResponseFormat = WebMessageFormat.Json,
        RequestFormat = WebMessageFormat.Json)]
   string StripeWebhook(Event data); 
}

当我为该服务创建实现时:

public class JsonService:IContract 
{
  public  string StripeWebhook(Event data)
  {  
    data.Dump("rcvd data");
    return "{\"result\":\"success\"}";
  }
}

并将其提供给 WebServiceHost:

// keeps the service running
AutoResetEvent are = new AutoResetEvent(false);

void Start() 
{
  //netsh http add urlacl url=http://+:8000/json user=USERNAME
  using (var serviceHost = new WebServiceHost(typeof(JsonService), new Uri("http://localhost:8000/json")))
  {
      serviceHost.AddServiceEndpoint(typeof(IContract), new WebHttpBinding(), "");
      serviceHost.Open();
      are.WaitOne(); // blocks 
      serviceHost.Close();
  }
}

我可以在启动后调用该服务,POST JSON 到它:

void Main()
{
    new Thread(Start).Start();  
    try {
        var wc = new WebClient();
        wc.Headers.Add("content-type","application/json");
        wc.UploadString("http://localhost:8000/json/Stripewebhook", File.ReadAllText(@"example_json_wcf.json")).Dump("response");
    } catch(WebException e) {
       e.Dump("raw");
       using(var ms = new MemoryStream()) {
           e.Response.GetResponseStream().CopyTo(ms);
           Encoding.UTF8.GetString(ms.ToArray()).Dump("error");
       }
    }
    Console.WriteLine("Now Running. Enter key will stop the service.");
    Console.ReadLine();
    are.Set();
}

这是我在 LinqPad 中的结果:

切记:

  • 裸露或包裹很重要
  • 在(反)序列化套管中很重要

如果反序列化失败,请先尝试序列化您的对象树,然后将该结果与您实际需要的结果进行比较。
或者使用许多 JSON 到 POCO 服务中的一个,例如:http://json2csharp.com/(我不隶属于该服务,它恰好是我的 Google 搜索中出现的第一个)