Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf
61 changes: 57 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,60 @@
# OS files

.DS_Store
coverage/
dist/
build/
.tmp/
Thumbs.db

# Logs

*.log

# Environment files

.env
.env.*

# Java / Maven

target/
*.class
.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

# Build outputs

build/
dist/
coverage/
.tmp/
!**/src/main/**/build/
!**/src/test/**/build/

# Spring / STS

HELP.md
.apt_generated
.classpath
.factorypath
.project
.settings/
.springBeans
.sts4-cache/

# IntelliJ IDEA

.idea/
*.iws
*.iml
*.ipr

# NetBeans

/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

# VS Code

.vscode/
3 changes: 3 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
57 changes: 57 additions & 0 deletions AI_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# AI Usage Note

## Tool used

ChatGPT

## How I used it

I used ChatGPT as a planning, review, and implementation-support assistant while working on this assignment.

I worked design-first. I first prepared the initial approach and decided the main solution direction myself: wallet-to-wallet transfer flow, idempotency handling, transaction boundaries, ledger entries, duplicate request handling, and failed transfer handling.

I used AI mainly to improve clarity, validate edge cases, and speed up repetitive implementation work.

Specifically, I used it to:

* Review the transfer flow from the initial approach document.
* Improve the clean service flow for successful and failed transfers.
* Reason about retry-safe behavior using a separate idempotency record.
* Decide how expected business failures like insufficient balance should be recorded.
* Review error scenarios such as invalid wallet, insufficient balance, duplicate request, and concurrent debit attempts.
* Plan test cases based on the approach document.
* Generate and refine integration test scenarios for success, failure, duplicate idempotency key, ledger entry validation, and concurrent transfer cases.
* Improve documentation wording for README and implementation explanation.
* Review local test failures at a high level and correct the implementation accordingly.

I reviewed and corrected the suggestions before applying them. I implemented, ran, debugged, and verified the final solution locally.

## Representative prompts

These are representative prompts similar to what I used during the session:

1. Help me understand the wallet transfer assignment expectations around idempotency, retries, concurrency, transaction safety, database design, and testing.

2. Help me structure an initial approach document for a wallet-to-wallet transfer assignment where duplicate requests, retries, failed transfers, and concurrent debits need to be handled correctly.

3. Review this successful transfer flow: validate request, claim idempotency key, create transfer as pending, lock involved wallets, debit source, credit destination, create ledger entries, mark transfer processed, and store the final result.

4. Review this failed transfer flow: if the source wallet has insufficient balance, mark the transfer as failed, do not create ledger entries, and store the failed result so retries return the same response.

5. Help me decide whether a business failure should be rolled back or committed as a failed transfer record.

6. Help me keep the controller thin and move the main business logic into the service layer.

7. Review the clean code flow for the transfer service so that idempotency handling, validation, wallet updates, ledger entries, and response creation are easy to follow.

8. Help me identify possible error scenarios for this wallet transfer service, including invalid wallet, insufficient balance, duplicate request, and concurrent transfer attempts.

9. Help me design integration tests based on my approach document for successful transfer, insufficient balance, invalid wallet, duplicate idempotency key, ledger entries, and concurrent transfers from the same wallet.

10. Help me refine the test assertions so they verify balances, transfer status, ledger entries, and idempotency behavior correctly.

11. Help me prepare a concise README explaining the implementation, idempotency strategy, transaction strategy, concurrency handling, API, and tests.

## Validation

I ran the test suite locally and verified that the implemented scenarios passed before submitting the solution.
101 changes: 101 additions & 0 deletions APPROACH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Initial Understanding and First Approach Ideas

## Problem:

We need to ensure wallet-to-wallet transferring of funds without duplication, correctly handling/reporting failures, and maintaining correct balances.

## Approach:

### Points of concern:

1. Preventing duplicate transfers when same idempotencyKey is retried.
2. Double ledger entries for each transaction: one debit and one credit.
3. Keeping change in balance correct under concurrent transfer requests.
4. Handling transfer states safely using PENDING, PROCESSED and FAILED.
5. Multiple Transfers from single wallet should happen sequentially (no 2 debits at once).
6. Avoid concurrent attempt to debit from single wallet (one after another transaction flow).

## First thought of approach:

### A: Successful Transaction

1. Validate incoming API request.
2. Insert/claim an idempotency record using provided idempotencyKey with UNIQUE constraint and status IN_PROGRESS.
3. If idempotencyKey already exists, return the stored result/status for COMPLETED/FAILED or return still processing response for IN_PROGRESS.
4. Start the wallet transfer database transaction.
5. Create a transfer Record with Pending Status.
6. Lock or safely update the wallet rows to prevent concurrent attempts of debit.
7. Validate the amount of debit availability in wallet.
8. Debit from source wallet and Credit to Destination wallet.
9. Insert debit and credit ledger entries.
10. Mark transfer Record as Processed status.
11. Update the idempotency record to COMPLETED and store final idempotency result.
12. Commit Transaction.

### B: Failed Transaction

1. Validate field request before starting the transfer.
2. If request is invalid, return a validation error.
3. Insert/claim an idempotency record using provided idempotencyKey with UNIQUE constraint and status IN_PROGRESS.
4. If idempotencyKey already exists, return the stored result/status for COMPLETED/FAILED or return still processing response for IN_PROGRESS.
5. Start the wallet transfer database transaction.
6. Create a transfer record with status PENDING.
7. Lock the wallet rows to prevent concurrent attempts of debit.
8. Validate wallet existence and source wallet balance inside the transaction.
9. If the transfer cannot be completed, mark the transfer as FAILED.
10. Do not debit/credit wallet balances.
11. Do not create successful debit/credit ledger entries.
12. For expected business failures, commit the transaction for FAILED records after storing the failed idempotency result.
13. For unexpected technical errors, rollback the transaction so partial wallet updates or ledger entries are not persisted.

## Database Design Direction:

I am considering PostgreSQL tables such as:

* wallets
* transfers
* ledger_entries
* idempotency_records

### A: wallets:

1. wallet_id as primary key
2. balance always non-negative, enforced with a CHECK constraint such as balance >= 0.

### B: transfers

1. transfer_id as primary key
2. source_wallet_id and destination_wallet_id as foreign keys to wallets.
3. status can only have PENDING, PROCESSED, FAILED.
4. Indexes on: source_wallet_id and destination_wallet_id

### C: ledger_entries

1. entry_id as primary key
2. transfer_id as foreign key to transfers.
3. wallet_id as foreign key to wallets.
4. transaction_type can only have DEBIT and CREDIT.
5. Index on: transfer_id

### D: idempotency_records:

The idempotency_records table will help make the API retry-safe by storing the idempotencyKey, processing status, transfer reference, and final result details needed to return the same result for duplicate retries.

1. idempotency_record_id as primary key
2. idempotencyKey with UNIQUE constraint
3. status can only have IN_PROGRESS, COMPLETED, and FAILED.
4. transfer_id as foreign key to transfers.
5. result_status/result_message to return previous result for duplicate retries.
6. Index on: idempotencyKey

Common to all entities/table: createdAt/updatedAt

## Testing Strategies:

1. All correct data request for Successful order.
2. Invalid wallet scenario.
3. duplicate request with same idempotencyKey.
4. Ledger entry validation.
5. Insufficient balance case.
6. Concurrent transfer attempts from the same wallet.
7. Retry-safe behaviour where a request is repeated after simulated timeout or duplicate delivery.
Loading