fix(s3): handle charge.succeeded webhook event type (STA-194)#184
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughAdds handling for a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
WebhookCommandHandler only handled payment_intent.succeeded but not charge.succeeded. When Stripe sends a charge.succeeded webhook it now routes to the same handlePaymentSucceeded flow and the idempotency check covers both event types. Also corrects the E2E StripeWebhookSender payload to use charge.succeeded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e64e159 to
ad75ba7
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
phase3-integration-tests/src/test/java/com/stablecoin/payments/phase3/support/StripeWebhookSender.java (1)
72-88:⚠️ Potential issue | 🟠 MajorFix webhook payload schema to match Stripe's
charge.succeededevent structure.Line 81 incorrectly emits
"object": "payment_intent"for acharge.succeededevent. Per Stripe API docs,data.objectmust be a Charge resource with"object": "charge", and the PaymentIntent reference should reside atdata.object.payment_intent. This mismatch produces a non-representative test payload that can mask webhook parser defects.Proposed fix
private String buildChargeSucceededPayload(String pspReference, long amount, String currency) { return """ { "id": "evt_%s", "object": "event", "type": "charge.succeeded", "data": { "object": { "id": "%s", - "object": "payment_intent", + "object": "charge", + "payment_intent": "%s", "amount": %d, "currency": "%s", "status": "succeeded" } } } - """.formatted(pspReference, pspReference, amount, currency); + """.formatted(pspReference, pspReference, pspReference, amount, currency); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@phase3-integration-tests/src/test/java/com/stablecoin/payments/phase3/support/StripeWebhookSender.java` around lines 72 - 88, The payload built by buildChargeSucceededPayload is using the wrong resource type; change the JSON under data.object to represent a Stripe Charge (set "object": "charge") and add a "payment_intent" property pointing to a PaymentIntent id (either pass a paymentIntentId into buildChargeSucceededPayload or derive one, e.g. "pi_"+pspReference), then update the formatted placeholders to include that payment intent id so the emitted webhook matches Stripe's charge.succeeded schema.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@fiat-on-ramp/fiat-on-ramp/src/test/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandlerTest.java`:
- Around line 145-160: The test should also assert that no persistence of PSP
transactions occurs for duplicate charge.succeeded webhooks: in the test method
shouldSkipDuplicateChargeSucceededWebhookForAlreadyCollectedOrder() add an
assertion that pspTransactionRepository had no interactions (or did not save any
transaction) after calling handler.handleWebhook(command), similar to the
existing then(orderRepository).should(never()).save(...) and
then(eventPublisher).shouldHaveNoInteractions(); reference
pspTransactionRepository to locate the mock and use
then(pspTransactionRepository).shouldHaveNoInteractions() or
then(pspTransactionRepository).should(never()).save(...) as appropriate.
In
`@fiat-on-ramp/fiat-on-ramp/src/testFixtures/java/com/stablecoin/payments/onramp/fixtures/WebhookFixtures.java`:
- Around line 111-120: The test helper aChargeSucceededCommand() is constructing
a WebhookCommand with EVENT_TYPE_CHARGE_SUCCEEDED but reuses
aSucceededEventJson(), which emits a payment_intent.succeeded payload—replace
the raw JSON payload with one that matches the charge.succeeded shape (create or
call aChargeSucceededEventJson() or similar) so the payload type aligns with the
EVENT_TYPE_CHARGE_SUCCEEDED used by aChargeSucceededCommand() and downstream
parsers/auditors like WebhookCommand consumers.
---
Outside diff comments:
In
`@phase3-integration-tests/src/test/java/com/stablecoin/payments/phase3/support/StripeWebhookSender.java`:
- Around line 72-88: The payload built by buildChargeSucceededPayload is using
the wrong resource type; change the JSON under data.object to represent a Stripe
Charge (set "object": "charge") and add a "payment_intent" property pointing to
a PaymentIntent id (either pass a paymentIntentId into
buildChargeSucceededPayload or derive one, e.g. "pi_"+pspReference), then update
the formatted placeholders to include that payment intent id so the emitted
webhook matches Stripe's charge.succeeded schema.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 78119700-8884-4325-becb-0c24db0e30c4
📒 Files selected for processing (4)
fiat-on-ramp/fiat-on-ramp/src/main/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandler.javafiat-on-ramp/fiat-on-ramp/src/test/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandlerTest.javafiat-on-ramp/fiat-on-ramp/src/testFixtures/java/com/stablecoin/payments/onramp/fixtures/WebhookFixtures.javaphase3-integration-tests/src/test/java/com/stablecoin/payments/phase3/support/StripeWebhookSender.java
…pTransaction assertion (STA-194) - Add dedicated aChargeSucceededEventJson() with correct type field - Add pspTransactionRepository.shouldHaveNoInteractions() to duplicate test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (2)
fiat-on-ramp/fiat-on-ramp/src/testFixtures/java/com/stablecoin/payments/onramp/fixtures/WebhookFixtures.java (1)
111-120:⚠️ Potential issue | 🟠 Major
aChargeSucceededCommand()uses a mismatched raw payload type.Line 119 still uses
aSucceededEventJson()(payment_intent.succeeded) while the command type ischarge.succeeded. Keep event type and raw payload consistent.Proposed fix
public static WebhookCommand aChargeSucceededCommand() { return new WebhookCommand( EVENT_ID, EVENT_TYPE_CHARGE_SUCCEEDED, PSP_REFERENCE, COLLECTION_ID, new Money(new BigDecimal("1000.00"), "USD"), "succeeded", - aSucceededEventJson()); + aChargeSucceededEventJson()); } + +public static String aChargeSucceededEventJson() { + return """ + { + "id": "%s", + "type": "charge.succeeded", + "data": { + "object": { + "id": "%s", + "amount": 100000, + "currency": "usd", + "status": "succeeded", + "metadata": { + "collection_id": "%s" + } + } + } + } + """.formatted(EVENT_ID, PSP_REFERENCE, COLLECTION_ID); +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fiat-on-ramp/fiat-on-ramp/src/testFixtures/java/com/stablecoin/payments/onramp/fixtures/WebhookFixtures.java` around lines 111 - 120, The helper aChargeSucceededCommand() constructs a charge.succeeded WebhookCommand but uses the mismatched raw payload aSucceededEventJson() (payment_intent.succeeded); update the raw payload to the correct charge payload by calling the appropriate fixture (e.g., aChargeSucceededEventJson()) or create a new aChargeSucceededEventJson() method and use that in aChargeSucceededCommand() so EVENT_TYPE_CHARGE_SUCCEEDED and the raw payload are consistent.fiat-on-ramp/fiat-on-ramp/src/test/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandlerTest.java (1)
145-160:⚠️ Potential issue | 🟠 MajorStrengthen duplicate-charge idempotency assertion for PSP transaction persistence.
Please also assert no interaction with
pspTransactionRepository; currently this test only covers order save/event publish side effects.Proposed test hardening
// then — no save or event publish should occur then(orderRepository).should(never()).save(eqIgnoringTimestamps(order)); then(eventPublisher).shouldHaveNoInteractions(); + then(pspTransactionRepository).shouldHaveNoInteractions();As per coding guidelines "Collection status tracking must handle webhook idempotency".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fiat-on-ramp/fiat-on-ramp/src/test/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandlerTest.java` around lines 145 - 160, The test should assert that no PSP transaction persistence occurs for duplicate charge webhooks; add an assertion in shouldSkipDuplicateChargeSucceededWebhookForAlreadyCollectedOrder to verify pspTransactionRepository has no interactions (e.g., then(pspTransactionRepository).shouldHaveNoInteractions()) after calling handler.handleWebhook(command) so the test covers both order and PSP-transaction idempotency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@fiat-on-ramp/fiat-on-ramp/src/test/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandlerTest.java`:
- Around line 145-160: The test should assert that no PSP transaction
persistence occurs for duplicate charge webhooks; add an assertion in
shouldSkipDuplicateChargeSucceededWebhookForAlreadyCollectedOrder to verify
pspTransactionRepository has no interactions (e.g.,
then(pspTransactionRepository).shouldHaveNoInteractions()) after calling
handler.handleWebhook(command) so the test covers both order and PSP-transaction
idempotency.
In
`@fiat-on-ramp/fiat-on-ramp/src/testFixtures/java/com/stablecoin/payments/onramp/fixtures/WebhookFixtures.java`:
- Around line 111-120: The helper aChargeSucceededCommand() constructs a
charge.succeeded WebhookCommand but uses the mismatched raw payload
aSucceededEventJson() (payment_intent.succeeded); update the raw payload to the
correct charge payload by calling the appropriate fixture (e.g.,
aChargeSucceededEventJson()) or create a new aChargeSucceededEventJson() method
and use that in aChargeSucceededCommand() so EVENT_TYPE_CHARGE_SUCCEEDED and the
raw payload are consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: bd186b1d-9f86-4757-bec0-37a47512c042
📒 Files selected for processing (4)
fiat-on-ramp/fiat-on-ramp/src/main/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandler.javafiat-on-ramp/fiat-on-ramp/src/test/java/com/stablecoin/payments/onramp/domain/service/WebhookCommandHandlerTest.javafiat-on-ramp/fiat-on-ramp/src/testFixtures/java/com/stablecoin/payments/onramp/fixtures/WebhookFixtures.javaphase3-integration-tests/src/test/java/com/stablecoin/payments/phase3/support/StripeWebhookSender.java
Summary
charge.succeededevent handling toWebhookCommandHandler(previously only handledpayment_intent.succeeded)StripeWebhookSenderE2E support class to send correct event typeCloses STA-194
Test plan
./gradlew :fiat-on-ramp:fiat-on-ramp:test— all tests pass./gradlew :fiat-on-ramp:fiat-on-ramp:spotlessCheck— clean🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests