[Stripe] Breaking Changes: "Payment Accident Prevention" using OpenAPI Diff + [Stripe] Webhooks: Money on the Line and Idempotency

API Ops & Cloud Integration
breaking-changewebhookstripemigrationgithub

[Stripe] Breaking Changes: "Payment Accident Prevention" using OpenAPI Diff

The scariest thing in payment system integration is "Payment failures occurring silently after a Stripe API version upgrade." Stripe uses date-based versioning rather than minor version updates, and checking changes one by one in the Changelog with your eyes is nearly impossible.

1. Symptoms

You upgraded the Stripe SDK version and deployed it, but the return object structure of a specific payment method (e.g., 3D Secure) changed, causing undefined errors and halting payments.

2. Solution: OpenAPI-based Diff Automation

Stripe publishes its OpenAPI Specification (Swagger) on GitHub. You must automate "Detecting changes before code modification" using this.

[Workflow]

1. Download Spec: Download the OpenAPI Spec JSON for the currently used version and the target version.
2. Run Diff Tool: Run a Diff using tools like openapicmd or optic.

[Validation Script Example (Shell)]

# Detect Breaking Changes
docker run --rm -v $(pwd):/specs openapitools/openapi-diff:latest \
  /specs/stripe_v2023-10-16.json \
  /specs/stripe_v2024-04-10.json \
  --markdown report.md

3. Validation

Check the generated report.md file for "Deleted Property" or "Type Changed" items. If a field our server depends on has been deleted, hold off on the SDK upgrade and modify the code first.


[Stripe] Webhooks: Money on the Line and Idempotency

Webhooks are the core method for notifying "Payment Success." However, if Stripe sends the same event 3 times (Retry) due to network timeouts, will our server issue points 3 times?

1. Dangerous Pattern (Anti-Pattern)

app.post('/webhook', (req, res) => {
  const event = req.body;
  if (event.type === 'payment_intent.succeeded') {
    givePoint(event.data.object.customer); // Dangerous: Potential for duplicate issuance
  }
  res.send({received: true});
});

This code is vulnerable to "Replay Attacks" and "Duplicate Processing."

2. Solution 1: Signature Verification

You must prevent hackers from sending fake payment success events. Be sure to use constructEvent from the Stripe SDK.

const sig = req.headers['stripe-signature'];
try {
  // rawBody usage is mandatory (Pre-JSON parsing state)
  event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
} catch (err) {
  return res.status(400).send(Webhook Error: ${err.message});
}

3. Solution 2: Idempotency Key Processing

You must save the Stripe Event ID (evt_...) in the database and query it before processing.

[Safe Logic]

1. Receive Event -> Extract evt_id.
2. Query evt_id in Redis/DB.
3. Exists? -> Return 200 OK and exit immediately (Already processed).
4. Doesn't exist? -> Execute business logic (Issue points) -> Save evt_id.

4. Validation

Forcefully send the same event twice using the Stripe CLI.

stripe trigger payment_intent.succeeded
stripe events resend evt_12345...

It is a success if "Skipping duplicate event" is printed in the server logs.