Updates and payments additions

This commit is contained in:
spiro-alvin-nyasimi
2026-07-02 16:45:48 +03:00
parent 36c25a8fe6
commit e9d216be78
98 changed files with 2623 additions and 1445 deletions

View File

@@ -0,0 +1,100 @@
package com.test.payment.models;
import java.util.List;
/**
* The database schema as code — executed statement-by-statement at startup by
* DatabaseSchemaInitializer (replaces the old classpath schema.sql).
*/
public final class DatabaseSchema {
private DatabaseSchema() {
}
public static final List<String> STATEMENTS = List.of(
"""
CREATE TABLE IF NOT EXISTS provider_tokens (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
provider VARCHAR(20) NOT NULL,
access_token VARCHAR(512) NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP NOT NULL
)
""",
"""
CREATE TABLE IF NOT EXISTS payment_initiations (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
provider VARCHAR(20) NOT NULL,
phone_number VARCHAR(15) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
account_reference VARCHAR(50),
transaction_desc VARCHAR(100),
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
)
""",
"""
CREATE INDEX IF NOT EXISTS idx_initiations_status_created
ON payment_initiations (status, created_at)
""",
// One response per initiation (UNIQUE on initiation_id enforces the 1:1 link).
// provider_reference is the ID used for callbacks/status checks
// (M-Pesa CheckoutRequestID, Airtel transaction id, MTN X-Reference-Id).
"""
CREATE TABLE IF NOT EXISTS payment_responses (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
initiation_id BIGINT NOT NULL UNIQUE,
provider VARCHAR(20) NOT NULL,
provider_reference VARCHAR(100) UNIQUE,
secondary_reference VARCHAR(100),
response_code VARCHAR(30),
response_description VARCHAR(255),
customer_message VARCHAR(255),
created_at TIMESTAMP NOT NULL,
CONSTRAINT fk_response_initiation FOREIGN KEY (initiation_id) REFERENCES payment_initiations (id)
)
""",
// One callback per initiation (UNIQUE on initiation_id enforces the 1:1 link)
"""
CREATE TABLE IF NOT EXISTS payment_callbacks (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
initiation_id BIGINT NOT NULL UNIQUE,
provider VARCHAR(20) NOT NULL,
provider_reference VARCHAR(100),
result_code VARCHAR(30),
result_desc VARCHAR(255),
receipt_number VARCHAR(50),
amount DECIMAL(10,2),
phone_number VARCHAR(15),
transaction_date VARCHAR(20),
raw_payload CLOB,
created_at TIMESTAMP NOT NULL,
CONSTRAINT fk_callback_initiation FOREIGN KEY (initiation_id) REFERENCES payment_initiations (id)
)
""",
// Consolidated transaction record, written when an initiation reaches a
// terminal state (SUCCESS/FAILED). UNIQUE initiation_id: one per initiation.
"""
CREATE TABLE IF NOT EXISTS transactions (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
initiation_id BIGINT NOT NULL UNIQUE,
provider VARCHAR(20) NOT NULL,
provider_reference VARCHAR(100),
secondary_reference VARCHAR(100),
phone_number VARCHAR(15),
amount DECIMAL(10,2),
account_reference VARCHAR(50),
status VARCHAR(20) NOT NULL,
result_code VARCHAR(30),
result_desc VARCHAR(255),
receipt_number VARCHAR(50),
transaction_date VARCHAR(20),
resolved_by VARCHAR(20),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP,
CONSTRAINT fk_transaction_initiation FOREIGN KEY (initiation_id) REFERENCES payment_initiations (id)
)
"""
);
}

View File

@@ -1,25 +0,0 @@
package com.test.payment.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MpesaResponse {
@JsonProperty("MerchantRequestID")
private String merchantRequestID;
@JsonProperty("ResponseCode")
private String responseCode;
@JsonProperty("CheckoutRequestID")
private String checkoutRequestId;
@JsonProperty("CustomerMessage")
private String customerMessage;
@JsonProperty("ResponseDescription")
private String responseDescription;
}

View File

@@ -0,0 +1,33 @@
package com.test.payment.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table("PAYMENT_CALLBACKS")
public class PaymentCallback {
@Id
private Long id;
private Long initiationId;
private String provider;
private String providerReference;
private String resultCode;
private String resultDesc;
private String receiptNumber;
private BigDecimal amount;
private String phoneNumber;
private String transactionDate;
private String rawPayload;
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,30 @@
package com.test.payment.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table("PAYMENT_INITIATIONS")
public class PaymentInitiation {
@Id
private Long id;
private String provider;
private String phoneNumber;
private BigDecimal amount;
private String accountReference;
private String transactionDesc;
private String status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,7 @@
package com.test.payment.models;
public enum PaymentProviderType {
MPESA,
AIRTEL,
MTN
}

View File

@@ -1,12 +0,0 @@
package com.test.payment.models;
import lombok.Data;
@Data
public class PaymentRequest {
private long phoneNumber;
private int amount;
private String accountReference;
private String transactionDesc;
}

View File

@@ -0,0 +1,35 @@
package com.test.payment.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import java.time.LocalDateTime;
/**
* The provider's answer to an initiation. providerReference is the ID later used
* for callbacks and status checks (M-Pesa CheckoutRequestID, Airtel transaction id,
* MTN X-Reference-Id); secondaryReference is any additional provider ID
* (M-Pesa MerchantRequestID).
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table("PAYMENT_RESPONSES")
public class PaymentResponse {
@Id
private Long id;
private Long initiationId;
private String provider;
private String providerReference;
private String secondaryReference;
private String responseCode;
private String responseDescription;
private String customerMessage;
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,25 @@
package com.test.payment.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table("PROVIDER_TOKENS")
public class ProviderToken {
@Id
private Long id;
private String provider;
private String accessToken;
private LocalDateTime expiresAt;
private LocalDateTime createdAt;
}

View File

@@ -1,23 +1,42 @@
package com.test.payment.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.*;
import org.springframework.data.relational.core.mapping.Table;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* Consolidated record written when a payment attempt reaches a terminal state
* (SUCCESS or FAILED) — one row per initiation, whatever path resolved it
* (callback, status query, immediate rejection, or reconciliation timeout).
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table("transactions")
@Table("TRANSACTIONS")
public class Transaction {
@Id
private String id;
private long phoneNumber;
private long amount;
private Long id;
private Long initiationId;
private String provider;
private String providerReference;
private String secondaryReference;
private String phoneNumber;
private BigDecimal amount;
private String accountReference;
private String status;
private String checkoutRequestId;
private String resultCode;
private String resultDesc;
private String receiptNumber;
private String transactionDate;
private String resolvedBy;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,7 @@
package com.test.payment.models;
public enum TransactionStatus {
PENDING,
SUCCESS,
FAILED
}