Commit b5d45fd
feat(s1): wire Phase 3 activities + E2E testing — first real payment (STA-18) (#180)
* feat(s1): wire Phase 3 activities into payment saga (STA-18)
Wire S3, S4, S5 into Temporal PaymentWorkflow for full sandwich flow.
- Add 3 activity interfaces + 8 DTOs in domain layer
- Add 3 activity impls in infrastructure (Feign → S3/S4/S5)
- Add PaymentSignalConsumer for fiat.collected + chain.transfer.confirmed
- Full 5-step saga with LIFO compensation
- 11 workflow tests (7 new Phase 3 scenarios)
Closes STA-18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(s1): add Phase 3 E2E test module with full sandwich scenarios (STA-18)
Add phase3-integration-tests module with 3 E2E scenarios:
- Happy path full sandwich (USD→USDC→EUR)
- Compliance rejection (WireMock sanctions hit)
- Idempotency (duplicate key returns 200)
Includes test support: PaymentApiClient, ServiceHealthChecker,
WireMockAdminClient, StripeWebhookSender, ModulrWebhookSender,
KafkaEventVerifier. Run via scripts/run-phase3-tests.sh.
Closes STA-18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): resolve Docker Compose startup issues for Phase 3 E2E stack (STA-18)
- Add @ConditionalOnMissingBean on S4 fallbackCustodyEngine and S5 clock
- Add @Autowired to DevCustodyAdapter constructor (Spring Boot 4 multi-constructor)
- Add -parameters compiler flag globally
- Enable fallback adapters for S2 and S6 in compose
- Increase healthcheck start_period to 180s and retries to 60
- Increase run script max_wait to 420s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): resolve S4 KafkaTemplate and S6 enum query issues (STA-18)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): use readiness endpoint and service_started for orchestrator deps (STA-18)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): fix E2E pipeline — S6 releaseLock endpoint, ARM64 images, Kafka consumer, Stripe URL (STA-18)
- Add DELETE /v1/fx/lock/{lockId} endpoint to S6 (releaseLock)
- Add LockNotFoundException + handler in S6
- Fix FxRateLockJpaRepository to use enum type instead of String
- Add Kafka consumer config for S1 PaymentSignalConsumer
- Fix Stripe base URL (remove double /v1 path)
- Fix CollectionRequest required fields (pspId, accountType, etc.)
- Switch all Jib configs to docker:// prefix for native ARM64 images
- Add -parameters compiler flag globally
- Fix S4 DevCustodyAdapter @Autowired + spring-cloud-stream-binder-kafka
- Fix S5 FallbackAdaptersConfig @ConditionalOnMissingBean on clock
- Update E2E tests to poll S3 for collection order + wait for terminal state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(s1): sync Temporal workflow terminal state to DB via UpdatePaymentStateActivity (STA-193)
Add UpdatePaymentStateActivity to sync workflow terminal state back to
the Payment aggregate in PostgreSQL. This ensures GET /v1/payments/{id}
returns COMPLETED/FAILED after the Temporal workflow finishes.
- Add WORKFLOW_COMPLETED trigger for direct INITIATED → COMPLETED transition
- Add Payment.completeFromWorkflow() domain method
- Add UpdatePaymentStateActivity interface + impl (hexagonal pattern)
- Call syncStateToDb() before every return in PaymentWorkflowImpl
- Idempotent: skip if payment already in terminal state (Temporal replay)
Closes STA-193
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): correct webhook endpoint paths for S3 and S5 (STA-18)
S3 webhook: /internal/webhooks/psp/stripe (not /v1/webhooks/stripe)
S5 webhook: /internal/webhooks/partner (not /v1/webhooks/modulr)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): use pspReference from S3 collection response in Stripe webhook (STA-18)
S3 WebhookCommandHandler looks up collections by pspReference, not
collectionId. The test webhook must use the actual pspReference
(pi_test_12345) as the charge ID.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): remove ch_ prefix from webhook charge ID to match pspReference (STA-18)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): add spring-cloud-stream-binder-kafka for KafkaListener auto-config (STA-18)
Without this, @KafkaListener annotations in PaymentSignalConsumer are
not detected — KafkaAutoConfiguration doesn't create the listener
container factory.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): unique Stripe pspReference via WireMock templating + spring-boot-starter-kafka (STA-18)
WireMock Stripe stub now returns unique pi_<UUID> for each payment
intent, preventing duplicate pspReference errors in S3 webhook lookup.
S1 uses spring-boot-starter-kafka (not cloud-stream-binder) for
@KafkaListener auto-configuration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): use payment_intent.succeeded event type matching S3 handler (STA-194)
S3 WebhookCommandHandler matches 'payment_intent.succeeded', not
'charge.succeeded'. Updated webhook sender payload accordingly.
Also updated STA-194: the bug was in the test payload, not S3 code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): provide default wallet address and chain for chain transfer (STA-18)
S4 requires toWalletAddress — use a default recipient wallet address
and 'base' as preferred chain for MVP E2E testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): use FORWARD transfer type matching S4 TransferType enum (STA-18)
S4 expects FORWARD/RETURN, not PAYMENT. Also fixes return transfer
type from RETURN which is correct.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): remove APP_CUSTODY_PROVIDER=dev to use fallback custody engine (STA-18)
DevCustodyAdapter requires a real EVM private key for signing.
FallbackCustodyEngine returns mock results suitable for E2E testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): override app.custody.provider=fallback to skip DevCustodyAdapter (STA-18)
S4 application.yml defaults to 'dev' which loads DevCustodyAdapter
(needs real EVM private key). Override to 'fallback' so neither
DevCustodyAdapter nor FireblocksCustodyAdapter loads, and the
FallbackCustodyEngine bean is used instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): provide required off-ramp fields — recipientAccountHash, partner ID/name (STA-18)
S5 PayoutRequest requires recipientAccountHash, offRampPartnerId,
and offRampPartnerName as @notblank fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): add bank account details for SEPA off-ramp payout (STA-18)
S5 requires bankAccountNumber + bankCode for FIAT payout type.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): pass sourceAmount as redeemedAmount for off-ramp (STA-18)
redeemedAmount = USDC amount (≈ source USD amount, 1:1 peg).
S5 computes fiatAmount = redeemedAmount × appliedFxRate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s5): fallback redemption at 1:1 rate — USDC redemption returns face value (STA-18)
The fallback RedemptionGateway was multiplying by 0.92 which conflicts
with S5's fiat amount validation (redeemedAmount × appliedFxRate).
In production, Circle returns actual fiat received. For fallback,
1:1 is correct (USDC ≈ USD at face value).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): pass target amount + rate 1.0 for off-ramp — FX already applied (STA-18)
The FX conversion is already done (sourceAmount USD → targetAmount EUR).
Off-ramp receives the EUR amount and redeems at 1:1. The appliedFxRate
for the off-ramp step should be 1.0 (identity — no further conversion).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): provide all required FxRate fields in UpdatePaymentStateActivity (STA-18)
FxRate compact constructor validates all fields are non-null.
Provide from/to currencies, expiry, and provider for the
workflow-synced FxRate object.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): replace workflow failure test with second payment test (STA-18)
With all fallback adapters, payments always succeed — no way to trigger
FAILED state. Replace with a second payment test that validates
concurrent workflow execution and sends its own Stripe webhook.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(s1): update business tests for full 5-step saga with Phase 3 activities (STA-18)
Register all 7 activities in TestWorkflowEnvironment, add mock stubs
for S3/S4/S5 Feign clients with signal delivery, update state
assertions for COMPLETED workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(s1): replace inline FQNs with proper imports per coding standards (STA-18)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: remove accidentally committed duplicate files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(infra): address CodeRabbit review — idempotent topics, healthcheck, log fixes (STA-18)
- Add --if-not-exists to rpk topic create commands
- Fix Temporal healthcheck (exit 1 on failure, not exit 0)
- Fix activity count log (7 not 6)
- Fix FX failure events labeled as FX_LOCKING (not COMPLIANCE_CHECK)
- Fix S6 releaseLock log only when pool found
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 39a5c07 commit b5d45fd
62 files changed
Lines changed: 3043 additions & 117 deletions
File tree
- api-gateway-iam/api-gateway-iam
- blockchain-custody/blockchain-custody
- src/main/java/com/stablecoin/payments/custody
- config
- infrastructure/provider/dev
- compliance-travel-rule/compliance-travel-rule
- fiat-off-ramp/fiat-off-ramp
- src/main/java/com/stablecoin/payments/offramp/config
- fiat-on-ramp/fiat-on-ramp
- fx-liquidity-engine/fx-liquidity-engine
- src/main/java/com/stablecoin/payments/fx
- application
- controller
- service
- domain/exception
- infrastructure/persistence
- entity
- infra/local/wiremock
- __files
- mappings
- ledger-accounting/ledger-accounting
- merchant-iam/merchant-iam
- merchant-onboarding/merchant-onboarding
- payment-orchestrator/payment-orchestrator
- src
- business-test/java/com/stablecoin/payments/orchestrator
- main
- java/com/stablecoin/payments/orchestrator
- application/config
- domain
- model
- workflow
- activity
- infrastructure
- activity
- messaging
- resources
- testFixtures/java/com/stablecoin/payments/orchestrator/fixtures
- test/java/com/stablecoin/payments/orchestrator/domain/workflow
- phase3-integration-tests
- src/test
- java/com/stablecoin/payments/phase3
- support
- resources
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
94 | 95 | | |
95 | 96 | | |
96 | 97 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| |||
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
| 82 | + | |
81 | 83 | | |
82 | 84 | | |
83 | 85 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
42 | 43 | | |
43 | 44 | | |
44 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
67 | 71 | | |
68 | 72 | | |
69 | 73 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
0 commit comments