stripe / stripe-java

Java library for the Stripe API.
https://stripe.com
MIT License
819 stars 359 forks source link

Stripe cli signature verification not working #919

Closed giacomo-m closed 4 years ago

giacomo-m commented 4 years ago

Hi, i'm using Stripe CLI for testing my webhook.

event = Webhook.constructEvent(payload, <Stripe-Signature header>, <Stripe CLI signing secret>); always returns an exception "No signatures found matching the expected signature for payload".

Stripe CLI signing secret: whsec_2QF92N94RwqD5M6t553xec5ce3NGgfKs

Stripe-Signature header: t=1576858212,v1=63dbd81dd42f4b5406451e4add0b89357a62598deba8e1978e53d005487927a6,v0=3016f803483615a7f75716ca92c404f5e550eab968640a85c738f31c42109770

Payload: { "id": "evt_1Fro3MEErckdb3enb7aSN8js", "object": "event", "api_version": "2019-12-03", "created": 1576858211, "data": { "object": { "id": "pi_1Fro3KEErckdb3enotYuEhG1", "object": "payment_intent", "amount": 2000, "amount_capturable": 0, "amount_received": 2000, "application": null, "application_fee_amount": null, "canceled_at": null, "cancellation_reason": null, "capture_method": "automatic", "charges": { "object": "list", "data": [ { "id": "ch_1Fro3LEErckdb3ene1m27XhJ", "object": "charge", "amount": 2000, "amount_refunded": 0, "application": null, "application_fee": null, "application_fee_amount": null, "balance_transaction": "txn_1Fro3LEErckdb3ene7NN6V5C", "billing_details": { "address": { "city": null, "country": null, "line1": null, "line2": null, "postal_code": null, "state": null }, "email": null, "name": null, "phone": null }, "captured": true, "created": 1576858211, "currency": "usd", "customer": null, "description": "(created by Stripe CLI)", "destination": null, "dispute": null, "disputed": false, "failure_code": null, "failure_message": null, "fraud_details": { }, "invoice": null, "livemode": false, "metadata": { }, "on_behalf_of": null, "order": null, "outcome": { "network_status": "approved_by_network", "reason": null, "risk_level": "normal", "risk_score": 22, "seller_message": "Payment complete.", "type": "authorized" }, "paid": true, "payment_intent": "pi_1Fro3KEErckdb3enotYuEhG1", "payment_method": "pm_1Fro3KEErckdb3enJWQW6BYP", "payment_method_details": { "card": { "brand": "visa", "checks": { "address_line1_check": null, "address_postal_code_check": null, "cvc_check": null }, "country": "US", "exp_month": 12, "exp_year": 2020, "fingerprint": "RhCup5EEbm2AICiC", "funding": "credit", "installments": null, "last4": "4242", "network": "visa", "three_d_secure": null, "wallet": null }, "type": "card" }, "receipt_email": null, "receipt_number": null, "receipt_url": "https://pay.stripe.com/receipts/acct_1CaOCdEErckdb3en/ch_1Fro3LEErckdb3ene1m27XhJ/rcpt_GObFdv7k1cm1CvM4rAu2RGn6kTkYvtl", "refunded": false, "refunds": { "object": "list", "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/ch_1Fro3LEErckdb3ene1m27XhJ/refunds"
        },
        "review": null,
        "shipping": {
          "address": {
            "city": "San Francisco",
            "country": "US",
            "line1": "510 Townsend St",
            "line2": null,
            "postal_code": "94103",
            "state": "CA"
          },
          "carrier": null,
          "name": "Jenny Rosen",
          "phone": null,
          "tracking_number": null
        },
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "succeeded",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1Fro3KEErckdb3enotYuEhG1"
  },
  "client_secret": "pi_1Fro3KEErckdb3enotYuEhG1_secret_BVrTV5VdQq8zelcT76NAvMhFv",
  "confirmation_method": "automatic",
  "created": 1576858210,
  "currency": "usd",
  "customer": null,
  "description": "(created by Stripe CLI)",
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Fro3KEErckdb3enJWQW6BYP",
  "payment_method_options": {
    "card": {
      "installments": null,
      "request_three_d_secure": "automatic"
    }
  },
  "payment_method_types": [
    "card"
  ],
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": {
    "address": {
      "city": "San Francisco",
      "country": "US",
      "line1": "510 Townsend St",
      "line2": null,
      "postal_code": "94103",
      "state": "CA"
    },
    "carrier": null,
    "name": "Jenny Rosen",
    "phone": null,
    "tracking_number": null
  },
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "succeeded",
  "transfer_data": null,
  "transfer_group": null
}

}, "livemode": false, "pending_webhooks": 2, "request": { "id": "req_QxV95RJcUkjLaJ", "idempotency_key": null }, "type": "payment_intent.succeeded" }

API version: 2019-12-03

Can you help me?

Thanks. Giacomo

cjavilla-stripe commented 4 years ago

Hey Giacomo,

Thanks for reaching out. Can you share a bit more of your webhook handling code? One of the more common issues is that the payload is somehow formatted and not passed in as the raw string request body.

Also, we'd be happy to field questions like this at support.stripe.com/contact or on IRC where there are Stripe engineers available to chat during most business hours in the #stripe channel on Freenode (https://webchat.freenode.net/?channel=#stripe).

ob-stripe commented 4 years ago

Closing due to inactivity, but feel free to reply if you're still having trouble and we'll reopen.

JD-sixberries commented 4 years ago

I am using stripe cli and want to test webhooks locally. somehow my signatures didn't get verified. I don't know what happens with it. I follow the instruction given on stripe documentation.

my header of the request is some thing like below

{ host: 'localhost:8082', 'user-agent': 'Stripe/1.0 (+https://stripe.com/docs/webhooks)', 'content-length': '3646', accept: '/; q=0.5, application/xml', 'cache-control': 'no-cache', 'content-type': 'application/json; charset=utf-8', 'stripe-signature': 't=1594120195,v1=af8a2f86ad323bc33659b3adcc09a96230bb16087ef7722c4de21fcae098e6a0,v0=af2697576d5efdd27ab3f572b974c5b87a84804a0f327b5947909d6a22343dd6', 'accept-encoding': 'gzip', ip: '::ffff:127.0.0.1' }

I got the error something like below

No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing

any idea what's going wrong with it?

stripe version 1.4.4

FYI, I am just using the sample code from below stripe link

https://stripe.com/docs/webhooks/signatures#verify-manually

remi-stripe commented 4 years ago

@JD-sixberries The Stripe CLI has its own webhook secret so you need to make sure you are using that one properly. It's also important to ensure that your code or framework is not tampering with the raw data we sent you. The signature verification only works with the exact raw JSON, up to the exact new line or indentation space. Those 2 reasons are the most common reasons for this issue.

If you're still having problems after that, we recommend that you reach out to our support team: https://support.stripe.com/contact

JD-sixberries commented 4 years ago

@remi-stripe thanks for your suggestion. my code tampering the request data using overall body-parser into the JSON format. So I need to change the position of a webhooks route to the top of all other middleware into the server configuration

remi-stripe commented 4 years ago

Thanks for following up. I'm glad to hear you were able to narrow down the issue and resolve it on your end!

AAverin commented 4 years ago

@remi-stripe following this Node guide https://stripe.com/docs/payments/checkout/fulfill-orders I get exactly the same problem. Any ideas how to solve it?

remi-stripe commented 4 years ago

@AAverin I'd recommend talking to our support team for help on this: https://support.stripe.com/contact

rimiti commented 3 years ago

@JD-sixberries

I think that you have this error because your forgotten to use:

bodyParser.raw({ type: 'application/json' }),

This step is mandatory to avoid "Signature verification failures".

Simple and working example:

const webhooksStripeRouter: Router = Router();

webhooksStripeRouter.post(
  '/webhooks/stripe',
  bodyParser.raw({ type: 'application/json' }), // <==== don't forget this 
  async (req: Request, res: Response) => {
    try {
      // Retrieve the event by verifying the signature using the raw body and secret.
      let event: Stripe.Event;

      try {
        event = stripe.webhooks.constructEvent(
          req.body,
          req.headers['stripe-signature'],
          'whsec_jtfJGfWz8YqSwCUExxxxxxxxxxxy',
        );
      } catch (error) {
        throw new Error('Stripe webhook signature verification failed.');
      }

      // Extract the data from the event.
      const { data, type }: { data: Stripe.Event.Data; type: string } = event;

      if (type === 'payment_intent.succeeded') {
        // Cast the event into a PaymentIntent to make use of the types.
        const paymentIntent: Stripe.PaymentIntent = data.object as Stripe.PaymentIntent;
        // Funds have been captured
        // Fulfill any orders, e-mail receipts, etc
        // To cancel the payment after capture you will need to issue a Refund (https://stripe.com/docs/api/refunds).
        console.log(`🔔  Webhook received: ${paymentIntent.object} ${paymentIntent.status}!`);
        console.log('💰 Payment captured!');
      } else if (type === 'payment_intent.payment_failed') {
        // Cast the event into a PaymentIntent to make use of the types.
        const pi: Stripe.PaymentIntent = data.object as Stripe.PaymentIntent;
        console.log(`🔔  Webhook received: ${pi.object} ${pi.status}!`);
        console.log('❌ Payment failed.');
      }
      res.sendStatus(200);
    } catch (error) {
      console.debug(error);
    }
  },
);
JD-sixberries commented 3 years ago

@JD-sixberries

I think that you have this error because your forgotten to use:

bodyParser.raw({ type: 'application/json' }),

This step is mandatory to avoid "Signature verification failures".

Simple and working example:

const webhooksStripeRouter: Router = Router();

webhooksStripeRouter.post(
  '/webhooks/stripe',
  bodyParser.raw({ type: 'application/json' }), // <==== don't forget this 
  async (req: Request, res: Response) => {
    try {
      // Retrieve the event by verifying the signature using the raw body and secret.
      let event: Stripe.Event;

      try {
        event = stripe.webhooks.constructEvent(
          req.body,
          req.headers['stripe-signature'],
          'whsec_jtfJGfWz8YqSwCUExxxxxxxxxxxy',
        );
      } catch (error) {
        throw new Error('Stripe webhook signature verification failed.');
      }

      // Extract the data from the event.
      const { data, type }: { data: Stripe.Event.Data; type: string } = event;

      if (type === 'payment_intent.succeeded') {
        // Cast the event into a PaymentIntent to make use of the types.
        const paymentIntent: Stripe.PaymentIntent = data.object as Stripe.PaymentIntent;
        // Funds have been captured
        // Fulfill any orders, e-mail receipts, etc
        // To cancel the payment after capture you will need to issue a Refund (https://stripe.com/docs/api/refunds).
        console.log(`🔔  Webhook received: ${paymentIntent.object} ${paymentIntent.status}!`);
        console.log('💰 Payment captured!');
      } else if (type === 'payment_intent.payment_failed') {
        // Cast the event into a PaymentIntent to make use of the types.
        const pi: Stripe.PaymentIntent = data.object as Stripe.PaymentIntent;
        console.log(`🔔  Webhook received: ${pi.object} ${pi.status}!`);
        console.log('❌ Payment failed.');
      }
      res.sendStatus(200);
    } catch (error) {
      console.debug(error);
    }
  },
);

Thanks @rimiti I get the help when I reported or facing this issue. actually I convert my request data into the JSON which cause the issue otherwise it working as expected. So basically that was my bad during implementation

abdel-ships-it commented 1 year ago

Im using nestjs and was using @Body which parses the body, I tried being smart by turning it back into a string using JSON.stringify but this messed it up. So opted to using the raw body instead and it works!