Skip to main content
Version: v2

Commons Library — wallet-event-commons

Purpose

The wallet-event-commons library is the shared contract between all Convenient Checkout microservices. It contains DTOs, enums, event models, validation rules, and helper utilities that are consumed by every service in the ecosystem. Changes to this library propagate across the entire platform.

High-Impact Library

Every service depends on wallet-event-commons. A breaking change here breaks all services. Treat every PR to this library as a platform-wide change. See Change Management Rules below.


Repository

AttributeValue
Repowallet-event-commons
LanguageJava
Build toolMaven (pom.xml)
Package rootcom.optum.wallet.common
Consumed byAll wallet-* services

Package Structure

src/main/java/com/optum/wallet/common/
├── component/ ← Abstract base components (e.g., AbstractPaymentMethodDetails)
├── constants/ ← Static constants (MerchantEventTypes, PaymentMethodTypes, ErrorMessages)
├── dto/ ← Data Transfer Objects
│ ├── manufacturerCard/ ← Manufacturer/healthcare card DTOs
│ ├── merchant/ ← MerchantRequest, MerchantResponse
│ ├── telephonic/ ← Telephonic entry DTOs
│ └── ValidateResult.java
├── enums/ ← Shared enums (ALL cross-service enums live here)
│ ├── card/ ← CardBrand, CardChannel, CardFunding, CardNetwork, CardStatus
│ ├── dispute/ ← DisputeReason, DisputeStatus
│ ├── payment/ ← PaymentStatus, PaymentMethodType, PaymentCancellationReason, PaymentType, PaymentVendor
│ ├── refund/ ← RefundStatus, RefundReason, MerchantRefundStatus, MerchantRefundGroupStatus
│ ├── report/ ← ReportType, ReportStatus
│ ├── vendor/ ← Vendor, VendorChargeStatus, VendorPaymentMethodType
│ └── *.java ← Channel, ErrorGroup, ConsentCollectionType, etc.
├── event/ ← v1 event models (legacy — being superseded by events/v2/)
│ ├── builder/ ← EventBuilder
│ ├── dispute/dto/ ← Dispute event DTOs
│ ├── merchant/ ← MerchantEvent, MerchantPayload, MerchantSettings
│ ├── migration/dto/ ← Migration event DTOs
│ ├── notification/ ← NotificationEvent
│ ├── payment/dto/ ← v1 payment DTOs (Payment, ChargeDetails, Consent, etc.)
│ ├── paymentmethod/dto/ ← v1 payment method DTOs
│ ├── refund/dto/ ← v1 refund DTOs
│ ├── report/dto/ ← Report DTOs
│ ├── shared/ ← Shared event DTOs (Agent, EventContext, PhoneNumber, Vendor, error/)
│ └── *.java ← Event, EventStatus, ApiResponseStatus
├── events/ ← Top-level events (v1 + v2)
│ ├── v2/dto/ ← ★ V2 shared models
│ │ ├── error/ ← Problem, Remediation, RemediationType
│ │ ├── payment/ ← SplitPayment, PaymentAllocation, PaymentMethod, Card, USBankAccountDetails
│ │ └── refund/ ← MerchantRefundEventV2, RefundResponse, RefundAllocation, error/
│ └── *.java ← Event, EventBuilder, EventContext, EventStatus (v1 wrappers)
├── helper/ ← Helper utilities
│ └── manufacturerCard/ ← AgentHelper, CardHelper, MerchantConfigHelper, etc.
├── pojo/ ← Plain objects shared across domains
├── rules/ ← Business rule implementations
│ ├── agent/ ← AgentRule
│ ├── card/ ← DefaultCardRule, MFCardTypeRule
│ ├── medication/ ← Medication validation rules
│ └── merchantConfig/ ← MerchantConfigRule
├── util/ ← Utility classes (PaymentMethodUtils)
└── validation/ ← Validators (MedicationValidator, TestValidator)

Key Shared Enums

These enums define the canonical vocabulary across all services. Never define local alternatives.

ErrorGroup — Error Classification

public enum ErrorGroup {
CUSTOMER_ERROR,
PAYMENT_METHOD_ERROR,
PAYMENT_ERROR,
INVALID_REQUEST,
INTERNAL_ERROR,
WARNING,
NOT_FOUND,
UNAUTHORIZED,
INVALID_RESOURCE,
RETRIES_EXHAUSTED,
NOTIFICATION_ERROR,
SESSION_TIMEOUT,
SESSION_CANCELED,
FORBIDDEN,
UNPROCESSABLE_ENTITY,
PAYMENT_METHOD_MIGRATION_FAILED,
REFUND_ERROR
}

PaymentStatus — Payment Lifecycle

public enum PaymentStatus {
// In Progress
INITIATED, PROCESSING, PENDING,
PENDING_FOR_CUSTOMER_CREATION, PENDING_FOR_PAYMENT_METHOD_CREATION,
CANCEL_FAILED, PROCESSING_DEDUP_CHECK,

// Retriable
PAYMENT_METHOD_FAILED,

// Initialized
CAPTURE_INITIALIZED, CANCEL_INITIALIZED, CONFIRMATION_INITIALIZED,

// 3DS
AUTH_REQUIRED, CONFIRMATION_REQUIRED,

// Terminal — Success
ACCEPTED, AUTHORIZED, COMPLETED, PARTIAL_SUCCESS,

// Terminal — Failure
CANCELED, FAILED, NOT_INITIATED, ROLLED_BACK;
}

Other Critical Enums

EnumPackageUsed By
PaymentMethodTypeenums.paymentPayments, Customers, Wallet
PaymentCancellationReasonenums.paymentPayments
PaymentTypeenums.paymentPayments (SALE, PRE_AUTH)
PaymentVendorenums.paymentPayments, Adapter
RefundStatusenums.refundRefunds
MerchantRefundGroupStatusenums.refundRefund webhooks
CardBrandenums.cardPayments, Wallet, Reporting
DisputeStatusenums.disputeDisputes
ReportType / ReportStatusenums.reportReporting

V2 Shared DTOs

The events.v2.dto package contains the v2 data models used across services:

Problem — Standard Error Response

Based on RFC 7807, used by all v2 API error responses:

Problem.builder()
.title(ErrorGroup.PAYMENT_ERROR)
.status(422)
.detail("Payment processing failed for all records.")
.build();

Factory methods: Problem.build(detail), Problem.buildBadRequest(detail), Problem.buildNotFound(detail), Problem.from(ErrorResponse).

SplitPayment — v2 Payment Model

The canonical v2 payment DTO consumed by the payment service and webhook dispatcher:

FieldTypeDescription
idUUIDPlatform payment ID
amountLongTotal amount in cents
authorizedAmountLongTotal authorized
capturedAmountLongTotal captured
merchantTransactionIdStringIdempotency key
statusPaymentStatusCurrent state
paymentAllocationsList<PaymentAllocation>1-2 allocations
customerCustomerCustomer details
metadataMap<String, String>Custom key-value pairs
consentConsentACH consent details
errorErrorResponseError details (if failed)

Remediation — Rollback Context

Used in split-tender failures to describe what corrective action was taken:

Remediation.builder()
.type(RemediationType.CANCEL)
.message("Automatic cancellation due to sibling allocation failure")
.build();

Versioning & Deployment Strategy

Maven Coordinates

The library is published under:

<dependency>
<groupId>com.optum.ccg</groupId>
<artifactId>wallet-commons</artifactId>
<version>2.0.13</version>
</dependency>

Each consuming service pins the version through a property in its pom.xml:

<properties>
<wallet-commons.version>2.0.13</wallet-commons.version>
</properties>

<dependencies>
<dependency>
<groupId>com.optum.ccg</groupId>
<artifactId>wallet-commons</artifactId>
<version>${wallet-commons.version}</version>
</dependency>
</dependencies>

To check which version a service is currently running, look at the property value in its pom.xml or run:

./mvnw dependency:tree | grep wallet-commons

Why Staying Current Matters

wallet-commons is the contract layer between every service in the platform. When a new enum value is added — for example, a new PaymentStatus state — every service that deserialises payment events must be on a version that knows about that value. A service running an old version will throw a JSON deserialisation error when it receives an event containing the unknown value, causing silent failures or exceptions in production.

In practice:

  • A new PaymentStatus value is added in wallet-commons 2.1.0
  • wallet-stripe-adapter-service upgrades and starts publishing events with that new status
  • wallet-payment-service is still on 2.0.13 — it crashes deserialising those events

This is why upgrading wallet-commons promptly after a new release is not optional — it is a reliability requirement.


Semantic Versioning Rules

wallet-commons follows semantic versioning (MAJOR.MINOR.PATCH):

Change TypeVersion BumpWho Must ActExamples
PATCH2.0.13 → 2.0.14Optional upgrade; recommendedJavadoc fix, comment update, test addition — no API surface change
MINOR2.0.x → 2.1.0All services should upgrade; safe at own paceNew enum value, new optional DTO field, new class, new package
MAJOR2.x.x → 3.0.0All services must upgrade and coordinateRemoving a class or enum value, renaming a field, changing a field type

What a Breaking Change Looks Like — And Why It Is Dangerous

Understanding breaking changes is critical for engineers who modify wallet-commons. The following are real examples of changes that look harmless but break every consuming service.

Example 1 — Renaming an enum value

// BEFORE — in wallet-commons 2.0.x
public enum PaymentStatus {
COMPLETED, FAILED, CANCELLED
}

// AFTER — engineer renames for consistency (BREAKING CHANGE)
public enum PaymentStatus {
COMPLETED, FAILED, CANCELED // ← renamed from CANCELLED to CANCELED
}

Impact: Every service that has CANCELLED in its database, in serialised JSON events, or in switch statements will fail at runtime. Jackson cannot deserialise "CANCELLED" into the new enum. This is a MAJOR version bump and requires a coordinated migration across all services.

Safe alternative: Add the new value and deprecate the old one — never remove or rename:

public enum PaymentStatus {
COMPLETED,
FAILED,
CANCELLED, // keep existing value
/** @deprecated Use {@link #CANCELLED} — spelling variant retained for consistency */
@Deprecated
CANCELED // add alias if truly needed
}

Example 2 — Removing an optional field from a DTO

// BEFORE — wallet-commons 2.0.x
public class SplitPayment {
private String description; // optional field
}

// AFTER — engineer removes it thinking it's unused (BREAKING CHANGE)
public class SplitPayment {
// description removed
}

Impact: Any service that serialises a SplitPayment with a non-null description, or any consumer that reads the field, will fail to compile or produce unexpected nulls at runtime.

Safe alternative: Mark the field @Deprecated and stop populating it. Remove it only in the next MAJOR bump after confirming no service reads or writes it.

Example 3 — Changing a field type

// BEFORE — wallet-commons 2.0.x
public class PaymentAllocation {
private Long amount;
}

// AFTER — changing type (BREAKING CHANGE)
public class PaymentAllocation {
private BigDecimal amount; // ← type changed
}

Impact: Compile failure in every service. JSON deserialisation may also fail if the wire format changes. This is always a MAJOR bump and requires code changes in every consuming service before deployment.


Safe Change Patterns — What You Can Do Without Risk

ChangeSafe?Notes
Add a new enum value✅ MINORConsumers that don't use it are unaffected; consumers that deserialise events must upgrade
Add a new optional field to a DTO✅ MINORJackson ignores unknown fields by default — existing consumers continue working
Add a new class or package✅ MINORNo impact on existing code
Add a new @Deprecated annotation✅ PATCHCompiler warning only — no runtime impact
Fix a Javadoc comment✅ PATCHZero risk
Rename an enum value❌ MAJORRuntime deserialisation failures
Remove a field❌ MAJORCompile failures and potential NPEs
Remove an enum value❌ MAJORRuntime deserialisation failures and switch exhaustion
Change a field type❌ MAJORCompile failures
Change a field name❌ MAJORJSON deserialisation failures unless @JsonAlias added

V1/V2 Coexistence in the Library

The library currently ships both V1 and V2 packages in the same artifact. This is an intentional transitional state while services migrate from V1 to V2.

com.optum.wallet.common/
├── event/ ← V1 DTOs (active but in deprecation — do not use in new V2 code)
│ ├── payment/dto/
│ ├── refund/dto/
│ └── shared/
└── events/v2/ ← V2 DTOs (current — use in all new code)
└── dto/
├── error/ Problem, RefundApiErrorResponse
├── payment/ V2 payment DTOs
└── refund/ V2 refund DTOs, error envelopes
RuleDetail
New V2 codeImport from com.optum.wallet.common.events.v2.* only
Existing V1 codeMay continue using com.optum.wallet.common.event.* until explicitly migrated
No mixingDo not import from both event/ and events/v2/ in the same class
New additionsAll new shared types go into events/v2/ — never add to the V1 event/ package
EnumsPaymentStatus, RefundReason, ErrorGroup etc. are shared and version-agnostic — always import from com.optum.wallet.common.enums.*

Release Process

1. Branch from main in wallet-event-commons

2. Make additive change (new enum value, new class, etc.)

3. Bump pom.xml version (MAJOR / MINOR / PATCH as appropriate)

4. PR review — minimum 2 backend engineers; involve engineering manager for MAJOR bumps

5. Merge and publish to internal Maven repository

6. Open upgrade PRs in every consuming service

7. Each service upgrades, compiles, and runs full test suite

8. Deploy services — consumers of new values must deploy before publishers

For MAJOR bumps: services must be upgraded and deployed before any service starts emitting events or responses that use the new/changed API surface.


Worked Example: Adding a New Enum Value Safely

Suppose a new payment status DISPUTE_OPEN needs to be added.

Step 1 — Add the value to wallet-commons:

// In wallet-commons — PaymentStatus.java
public enum PaymentStatus {
// ... existing values ...
COMPLETED,
FAILED,
CANCELLED,
DISPUTE_OPEN // ← new value added
}

Step 2 — Bump the version (MINOR):

<!-- wallet-commons pom.xml -->
<version>2.1.0</version>

Step 3 — Publish to the internal Maven repository.

Step 4 — Upgrade all consuming services before any service starts emitting the new value:

<!-- In each service pom.xml -->
<wallet-commons.version>2.1.0</wallet-commons.version>

Step 5 — Handle the new value in switch statements. Any switch or if/else chain on PaymentStatus must be updated to handle DISPUTE_OPEN or compile warnings will surface:

// In wallet-payment-service — TransactionStateMachine.java
return switch (status) {
case COMPLETED -> handleCompleted();
case FAILED -> handleFailed();
case CANCELLED -> handleCancelled();
case DISPUTE_OPEN -> handleDisputeOpen(); // ← add handler
default -> throw new IllegalArgumentException("Unhandled status: " + status);
};

Step 6 — Run the full test suite in each service before merging the upgrade PRs.

Deploy order matters

The service that starts publishing events with DISPUTE_OPEN must deploy after all services that consume those events have upgraded to 2.1.0. Deploying the publisher first will cause deserialisation failures in consumers still on 2.0.x.


Upgrading a Service: Step-by-Step

For engineers picking up a wallet-commons upgrade PR:

# 1. Bump the version in pom.xml
# Change: <wallet-commons.version>2.0.13</wallet-commons.version>
# To: <wallet-commons.version>2.1.0</wallet-commons.version>

# 2. Compile and check for errors
./mvnw compile

# 3. Check for compiler warnings about deprecated types
./mvnw compile 2>&1 | grep -i "deprecated"

# 4. If new enum values were added, search for exhaustive switch statements
grep -r "PaymentStatus\|RefundStatus" src/ --include="*.java" \
| grep -i "switch\|case"

# 5. Run the full test suite
./mvnw test

# 6. If integration tests exist, run them too
./mvnw verify

If the compile step fails, cross-reference the wallet-commons changelog (in its CHANGELOG.md or release notes) to understand what changed and what code updates are required.


Deprecation Lifecycle

When a type needs to be removed, it goes through a mandatory deprecation window so consuming services have time to migrate:

Phase 1 — Mark deprecated (MINOR bump, e.g. 2.1.0)
Add @Deprecated(since = "2.1.0", forRemoval = true)
Add Javadoc pointing to the replacement
Services see compiler warnings — create migration tickets

Phase 2 — Migrate consuming services (within 2 sprints)
Each service replaces deprecated usage with the new type
Verified by checking for zero deprecation warnings on compile

Phase 3 — Remove (MAJOR bump, e.g. 3.0.0)
Deprecated type removed from the library
All services must already be migrated before this is published

Example of a properly deprecated class:

/**
* @deprecated Use {@link com.optum.wallet.common.events.v2.dto.error.Problem} instead.
* Will be removed in wallet-commons 3.0.0.
*/
@Deprecated(since = "2.1.0", forRemoval = true)
public class ErrorResponse implements Serializable {
private String code;
private String message;
}

Working With the Library Locally

To test an uncommitted wallet-commons change in a consuming service before publishing:

# In wallet-event-commons — install to local ~/.m2
./mvnw install -DskipTests

# In the consuming service — temporarily point to the local SNAPSHOT
# Change in pom.xml:
<wallet-commons.version>2.0.14-SNAPSHOT</wallet-commons.version>

# Run the service locally and verify the change works as expected
./mvnw spring-boot:run

# IMPORTANT — revert pom.xml before committing
# Never commit a SNAPSHOT version to any service
warning

A SNAPSHOT dependency in a committed pom.xml means the build is non-reproducible — CI will pull whatever snapshot exists at build time, which may differ between runs. This breaks deterministic deployments and makes rollbacks unreliable.


Change Management Rules

RuleDetail
No breaking changesNever remove or rename an enum value, field, or class that any service uses
Additive onlyAdd new enum values, new optional fields, new classes
Version bumpEvery change requires a Maven version bump in pom.xml
Downstream updateAfter publishing, update the dependency version in all consuming services
DeprecationMark deprecated items with @Deprecated and add Javadoc explaining the replacement. Minimum 2 sprint deprecation window before removal.
TestingAll new enums and DTOs must have unit tests in the commons library itself
PR reviewCommons PRs require review from at least 2 backend engineers; given the platform-wide impact, involve the engineering manager or tech lead when making structural changes

When to Add to Commons vs. Keep in Service

Put in CommonsKeep in Service
Enum used by ≥ 2 servicesEnum only meaningful in one service
DTO that crosses service boundaries (events, webhooks)Internal service DTOs (DB entities, internal mappers)
Validation rules that apply to multiple domainsDomain-specific business logic
Shared error structures (Problem, ErrorGroup)Service-specific error messages

Current State vs. Target State

AspectCurrent StateTarget StateMigration Priority
Package organizationMix of event/, events/, pojo/ with overlapping classesUnified v2/ package with clear domain separationP1
v1/v2 coexistencev1 DTOs in event/, v2 in events/v2/ — both activeDeprecate v1 DTOs; all services consume v2 onlyP2
Enum duplicationSome enums exist in both enums/ and event/shared/Single canonical location in enums/P1
Error modelErrorResponse (v1) and Problem (v2) coexistAll services use Problem for v2 responsesP2
Test coverageTests exist but coverage varies100% coverage on all enums, DTOs, and rulesP2
JavadocPartial documentationAll public classes and enums fully documentedP3