How to Receive Webhook from Stripe in Java
Asked Answered
S

4

12

I am trying to receive a webhook via a post request from Stripe Payments. The java method to process it looks like this:

@ResponseBody
@RequestMapping(    consumes="application/json",
                    produces="application/json",
                    method=RequestMethod.POST,
                    value="stripeWebhookEndpoint")
public String stripeWebhookEndpoint(Event event){

    logger.info("\n\n" + event.toString());

    logger.info("\n\n" + event.getId());

    return null;
}

But the Stripe Event always comes back with all null values:

<com.stripe.model.Event@315899720 id=null> JSON: {
  "id": null,
  "type": null,
  "user_id": null,
  "livemode": null,
  "created": null,
  "data": null,
  "pending_webhooks": null
}

If the method receives a String instead,and using @RequestBody:

@ResponseBody
@RequestMapping(    consumes="application/json",
                    produces="application/json",
                    method=RequestMethod.POST,
                    value="stripeWebhookEndpoint")
public String stripeWebhookEndpoint(@RequestBody String json){

    logger.info(json);

    return null;
}

Here, it prints the json without null values. Here's part of the request being printed:

{
  "created": 1326853478,
  "livemode": false,
  "id": "evt_00000000000000",
  "type": "charge.succeeded",
  "object": "event",
  "request": null,
  "data": {
    "object": {
      "id": "ch_00000000000000",
      "object": "charge",
      "created": 1389985862,
      "livemode": false,
      "paid": true,
      "amount": 2995,
      "currency": "usd",
...
}

But using @RequestBody with a Stripe Event parameter gives a 400: bad syntax.

So why can't I take in the correct type, a Stripe Event, as the parameter?

Seventh answered 17/1, 2014 at 23:4 Comment(2)
thanks for you question, But its is possible for hit localhost url from stripe?Outmarch
@HarmeetSinghTaara In order to check webhooks sent to your localhost, you could use a solution like ngrok.Storax
S
13

Here's what I did:

The Java method still takes in the Event as a json String. Then I used Stripe's custom gson adapter and got the Event with:

Event event = Event.gson.fromJson(stripeJsonEvent, Event.class);

Where stripeJsonEvent is the string of json taken in by the webhook endpoint.

Seventh answered 29/1, 2014 at 16:0 Comment(1)
since version 1.25 of the library, it's Event.GSON.fromJson(stripeJsonEvent, Event.class);Exogamy
B
11
public String stripeWebhookEndpoint(@RequestBody String json, HttpServletRequest request) {         
        String header = request.getHeader("Stripe-Signature");      
        String endpointSecret = "your stripe webhook secret";
        try {
            event = Webhook.constructEvent(json, header, endpointSecret);
            System.err.println(event);
        } catch (SignatureVerificationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         //
         enter code here
      return "";

}
Baro answered 3/10, 2019 at 3:14 Comment(2)
This is correctChryselephantine
Thank you for this. Your "@RequestBody String json" is what I was looking for, without using a Google dependency for Gson.Chalfant
A
7

I have been looking for the same answer, so after looking at their own code, here is how they actually do it:

String rawJson = IOUtils.toString(request.getInputStream());
Event event = APIResource.GSON.fromJson(rawJson, Event.class);

APIResource comes from their library (I am using 1.6.5)

Agger answered 29/1, 2014 at 11:49 Comment(3)
Hi Juan. +1 For using APIResource. I also have an answer on how to do this.Seventh
What package is the request from?Nitrile
how to get attributes of the child object of the event object. Lets say I want to get the subscription id from the event object. I couldn't directly access it by doing event.get....() as its not available.Heard
M
1

In order to abstract all of the deserialization logic out of the controller I did the following:

Created a custom deserializer

public class StripeEventDeserializer extends JsonDeserializer<Event> {

    private ObjectMapper mapper;
    
    public StripeEventDeserializer(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public Event deserialize(JsonParser jp, DeserializationContext context) throws IOException {
        ObjectNode root = mapper.readTree(jp);

        Event event = ApiResource.GSON.fromJson(root.toString(), Event.class);
        return event;
    }
}

I then needed to add that deserializer to my ObjectMapper config:

SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Event.class, new StripeEventDeserializer(mapper));
mapper.registerModule(simpleModule);

I could then use @RequestBody correctly on the Spring rest controller:

@PostMapping("/webhook")
public void webhook(@RequestBody Event stripeEvent)
Midrib answered 8/7, 2020 at 21:5 Comment(1)
What did you pass in request body? Please share the sample.Storax

© 2022 - 2024 — McMap. All rights reserved.