Skip to content

Commit abf2251

Browse files
committed
feat: add customer/payment method management and persistent provider mappings
1 parent e566a13 commit abf2251

13 files changed

Lines changed: 1159 additions & 38 deletions
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
CREATE TABLE provider_mappings (
2+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3+
entity_id VARCHAR(255) NOT NULL,
4+
entity_type VARCHAR(50) NOT NULL,
5+
provider_name VARCHAR(50) NOT NULL,
6+
provider_entity_id VARCHAR(255) NOT NULL,
7+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
8+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
9+
UNIQUE(entity_id, entity_type)
10+
);
11+
12+
CREATE INDEX idx_provider_mappings_entity ON provider_mappings(entity_id, entity_type);
13+
CREATE INDEX idx_provider_mappings_provider ON provider_mappings(provider_name);
14+
15+
CREATE TABLE customers (
16+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
17+
external_id VARCHAR(255) NOT NULL UNIQUE,
18+
email VARCHAR(255) NOT NULL,
19+
name VARCHAR(255),
20+
phone VARCHAR(50),
21+
metadata JSONB DEFAULT '{}',
22+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
23+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
24+
);
25+
26+
CREATE INDEX idx_customers_email ON customers(email);
27+
CREATE INDEX idx_customers_external_id ON customers(external_id);
28+
29+
CREATE TABLE payment_methods (
30+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
31+
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
32+
provider_name VARCHAR(50) NOT NULL,
33+
provider_payment_method_id VARCHAR(255) NOT NULL,
34+
type VARCHAR(50) NOT NULL,
35+
last4 VARCHAR(4),
36+
brand VARCHAR(50),
37+
exp_month INTEGER,
38+
exp_year INTEGER,
39+
is_default BOOLEAN DEFAULT false,
40+
metadata JSONB DEFAULT '{}',
41+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
42+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
43+
);
44+
45+
CREATE INDEX idx_payment_methods_customer ON payment_methods(customer_id);
46+
CREATE INDEX idx_payment_methods_provider ON payment_methods(provider_name, provider_payment_method_id);
47+
48+
CREATE TRIGGER update_provider_mappings_updated_at
49+
BEFORE UPDATE ON provider_mappings
50+
FOR EACH ROW
51+
EXECUTE FUNCTION update_updated_at_column();
52+
53+
CREATE TRIGGER update_customers_updated_at
54+
BEFORE UPDATE ON customers
55+
FOR EACH ROW
56+
EXECUTE FUNCTION update_updated_at_column();
57+
58+
CREATE TRIGGER update_payment_methods_updated_at
59+
BEFORE UPDATE ON payment_methods
60+
FOR EACH ROW
61+
EXECUTE FUNCTION update_updated_at_column();
62+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
-- Migration: Add fraud analysis results table
2+
-- Date: 2025-08-10
3+
-- Description: Create table for storing fraud detection analysis results
4+
5+
-- Create fraud analysis results table
6+
CREATE TABLE IF NOT EXISTS fraud_analysis_results (
7+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
8+
transaction_id VARCHAR(255) NOT NULL,
9+
user_id VARCHAR(255) NOT NULL,
10+
transaction_amount DECIMAL(10,2) NOT NULL,
11+
billing_country VARCHAR(3) NOT NULL,
12+
shipping_country VARCHAR(3) NOT NULL,
13+
ip_address VARCHAR(45) NOT NULL,
14+
transaction_velocity INTEGER NOT NULL,
15+
is_fraudulent BOOLEAN NOT NULL,
16+
fraud_score INTEGER NOT NULL CHECK (fraud_score >= 0 AND fraud_score <= 100),
17+
reason TEXT NOT NULL,
18+
allow BOOLEAN NOT NULL,
19+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
20+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
21+
);
22+
23+
-- Create indexes for fraud analysis results
24+
CREATE INDEX IF NOT EXISTS idx_fraud_analysis_transaction_id ON fraud_analysis_results(transaction_id);
25+
CREATE INDEX IF NOT EXISTS idx_fraud_analysis_user_id ON fraud_analysis_results(user_id);
26+
CREATE INDEX IF NOT EXISTS idx_fraud_analysis_created_at ON fraud_analysis_results(created_at);
27+
CREATE INDEX IF NOT EXISTS idx_fraud_analysis_is_fraudulent ON fraud_analysis_results(is_fraudulent);
28+
29+
-- Create trigger for fraud analysis results updated_at (if the function exists)
30+
DO $$
31+
BEGIN
32+
IF EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'update_updated_at_column') THEN
33+
DROP TRIGGER IF EXISTS update_fraud_analysis_results_updated_at ON fraud_analysis_results;
34+
CREATE TRIGGER update_fraud_analysis_results_updated_at
35+
BEFORE UPDATE ON fraud_analysis_results
36+
FOR EACH ROW
37+
EXECUTE FUNCTION update_updated_at_column();
38+
END IF;
39+
END
40+
$$;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
-- Audit tables for compliance and security
2+
CREATE TABLE audit_logs (
3+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4+
user_id VARCHAR(255),
5+
action VARCHAR(100) NOT NULL,
6+
resource_type VARCHAR(100) NOT NULL,
7+
resource_id VARCHAR(255) NOT NULL,
8+
old_values JSONB,
9+
new_values JSONB,
10+
ip_address INET,
11+
user_agent TEXT,
12+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
13+
);
14+
15+
-- Indexes for audit logs
16+
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
17+
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
18+
CREATE INDEX idx_audit_logs_resource_type ON audit_logs(resource_type);
19+
CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);
20+
21+
-- API keys table for authentication
22+
CREATE TABLE api_keys (
23+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
24+
key_hash VARCHAR(255) NOT NULL UNIQUE,
25+
user_id VARCHAR(255) NOT NULL,
26+
name VARCHAR(255) NOT NULL,
27+
permissions JSONB DEFAULT '[]',
28+
rate_limit_rps INTEGER DEFAULT 10,
29+
rate_limit_burst INTEGER DEFAULT 20,
30+
expires_at TIMESTAMP WITH TIME ZONE,
31+
last_used_at TIMESTAMP WITH TIME ZONE,
32+
is_active BOOLEAN DEFAULT true,
33+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
34+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
35+
);
36+
37+
-- Indexes for API keys
38+
CREATE INDEX idx_api_keys_user_id ON api_keys(user_id);
39+
CREATE INDEX idx_api_keys_key_hash ON api_keys(key_hash);
40+
CREATE INDEX idx_api_keys_is_active ON api_keys(is_active);
41+
CREATE INDEX idx_api_keys_expires_at ON api_keys(expires_at);
42+
43+
-- Rate limiting table
44+
CREATE TABLE rate_limits (
45+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
46+
key_hash VARCHAR(255) NOT NULL,
47+
endpoint VARCHAR(255) NOT NULL,
48+
requests_count INTEGER DEFAULT 0,
49+
window_start TIMESTAMP WITH TIME ZONE NOT NULL,
50+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
51+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
52+
UNIQUE(key_hash, endpoint, window_start)
53+
);
54+
55+
-- Indexes for rate limits
56+
CREATE INDEX idx_rate_limits_key_hash ON rate_limits(key_hash);
57+
CREATE INDEX idx_rate_limits_endpoint ON rate_limits(endpoint);
58+
CREATE INDEX idx_rate_limits_window_start ON rate_limits(window_start);
59+
60+
-- Security events table
61+
CREATE TABLE security_events (
62+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
63+
event_type VARCHAR(100) NOT NULL,
64+
severity VARCHAR(20) NOT NULL,
65+
user_id VARCHAR(255),
66+
ip_address INET,
67+
user_agent TEXT,
68+
details JSONB,
69+
resolved BOOLEAN DEFAULT false,
70+
resolved_at TIMESTAMP WITH TIME ZONE,
71+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
72+
);
73+
74+
-- Indexes for security events
75+
CREATE INDEX idx_security_events_type ON security_events(event_type);
76+
CREATE INDEX idx_security_events_severity ON security_events(severity);
77+
CREATE INDEX idx_security_events_user_id ON security_events(user_id);
78+
CREATE INDEX idx_security_events_created_at ON security_events(created_at);
79+
CREATE INDEX idx_security_events_resolved ON security_events(resolved);
80+
81+
-- Update triggers for audit logs
82+
CREATE OR REPLACE FUNCTION audit_trigger_function()
83+
RETURNS TRIGGER AS $$
84+
BEGIN
85+
IF TG_OP = 'DELETE' THEN
86+
INSERT INTO audit_logs (action, resource_type, resource_id, old_values)
87+
VALUES (TG_OP, TG_TABLE_NAME, OLD.id, row_to_json(OLD));
88+
RETURN OLD;
89+
ELSIF TG_OP = 'UPDATE' THEN
90+
INSERT INTO audit_logs (action, resource_type, resource_id, old_values, new_values)
91+
VALUES (TG_OP, TG_TABLE_NAME, NEW.id, row_to_json(OLD), row_to_json(NEW));
92+
RETURN NEW;
93+
ELSIF TG_OP = 'INSERT' THEN
94+
INSERT INTO audit_logs (action, resource_type, resource_id, new_values)
95+
VALUES (TG_OP, TG_TABLE_NAME, NEW.id, row_to_json(NEW));
96+
RETURN NEW;
97+
END IF;
98+
RETURN NULL;
99+
END;
100+
$$ LANGUAGE plpgsql;
101+
102+
-- Create audit triggers for critical tables
103+
CREATE TRIGGER payments_audit_trigger
104+
AFTER INSERT OR UPDATE OR DELETE ON payments
105+
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
106+
107+
CREATE TRIGGER refunds_audit_trigger
108+
AFTER INSERT OR UPDATE OR DELETE ON refunds
109+
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
110+
111+
CREATE TRIGGER subscriptions_audit_trigger
112+
AFTER INSERT OR UPDATE OR DELETE ON subscriptions
113+
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
114+
115+
CREATE TRIGGER disputes_audit_trigger
116+
AFTER INSERT OR UPDATE OR DELETE ON disputes
117+
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
118+
119+
-- Create updated_at triggers
120+
CREATE TRIGGER update_api_keys_updated_at
121+
BEFORE UPDATE ON api_keys
122+
FOR EACH ROW
123+
EXECUTE FUNCTION update_updated_at_column();
124+
125+
CREATE TRIGGER update_rate_limits_updated_at
126+
BEFORE UPDATE ON rate_limits
127+
FOR EACH ROW
128+
EXECUTE FUNCTION update_updated_at_column();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
CREATE TABLE provider_mappings (
2+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3+
entity_id VARCHAR(255) NOT NULL,
4+
entity_type VARCHAR(50) NOT NULL,
5+
provider_name VARCHAR(50) NOT NULL,
6+
provider_entity_id VARCHAR(255) NOT NULL,
7+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
8+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
9+
UNIQUE(entity_id, entity_type)
10+
);
11+
12+
CREATE INDEX idx_provider_mappings_entity ON provider_mappings(entity_id, entity_type);
13+
CREATE INDEX idx_provider_mappings_provider ON provider_mappings(provider_name);
14+
15+
CREATE TABLE customers (
16+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
17+
external_id VARCHAR(255) NOT NULL UNIQUE,
18+
email VARCHAR(255) NOT NULL,
19+
name VARCHAR(255),
20+
phone VARCHAR(50),
21+
metadata JSONB DEFAULT '{}',
22+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
23+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
24+
);
25+
26+
CREATE INDEX idx_customers_email ON customers(email);
27+
CREATE INDEX idx_customers_external_id ON customers(external_id);
28+
29+
CREATE TABLE payment_methods (
30+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
31+
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
32+
provider_name VARCHAR(50) NOT NULL,
33+
provider_payment_method_id VARCHAR(255) NOT NULL,
34+
type VARCHAR(50) NOT NULL,
35+
last4 VARCHAR(4),
36+
brand VARCHAR(50),
37+
exp_month INTEGER,
38+
exp_year INTEGER,
39+
is_default BOOLEAN DEFAULT false,
40+
metadata JSONB DEFAULT '{}',
41+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
42+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
43+
);
44+
45+
CREATE INDEX idx_payment_methods_customer ON payment_methods(customer_id);
46+
CREATE INDEX idx_payment_methods_provider ON payment_methods(provider_name, provider_payment_method_id);
47+
48+
CREATE TRIGGER update_provider_mappings_updated_at
49+
BEFORE UPDATE ON provider_mappings
50+
FOR EACH ROW
51+
EXECUTE FUNCTION update_updated_at_column();
52+
53+
CREATE TRIGGER update_customers_updated_at
54+
BEFORE UPDATE ON customers
55+
FOR EACH ROW
56+
EXECUTE FUNCTION update_updated_at_column();
57+
58+
CREATE TRIGGER update_payment_methods_updated_at
59+
BEFORE UPDATE ON payment_methods
60+
FOR EACH ROW
61+
EXECUTE FUNCTION update_updated_at_column();
62+

main.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,23 +210,28 @@ func main() {
210210

211211
printSuccess("Monitoring and alerting initialized")
212212

213-
printStep("7/10", "Initializing payment providers...")
214-
stripeProvider := providers.CreateStripeProvider(cfg.Stripe.Secret)
215-
xenditProvider := providers.CreateXenditProvider(cfg.Xendit.Secret)
216-
217-
providerSelector := providers.CreateMultiProviderSelector([]providers.PaymentProvider{stripeProvider, xenditProvider})
218-
printSuccess("Payment providers initialized")
219-
printInfo(" • Stripe: Ready for USD, EUR, GBP")
220-
printInfo(" • Xendit: Ready for IDR, SGD, MYR, PHP, THB, VND")
221-
222-
printStep("8/10", "Initializing stores...")
213+
printStep("7/10", "Initializing stores...")
223214
paymentRepo := stores.CreatePaymentRepository(database)
224215
planRepo := stores.CreatePlanRepository(database)
225216
subscriptionRepo := stores.CreateSubscriptionRepository(database)
226217
disputeRepo := stores.CreateDisputeRepository(database)
227218
fraudRepo := stores.CreateFraudRepository(database)
219+
customerStore := stores.CreateCustomerStore(database)
220+
paymentMethodStore := stores.CreatePaymentMethodStore(database)
221+
providerMappingStore := stores.CreateProviderMappingStore(database)
228222
printSuccess("Stores initialized")
229223

224+
printStep("8/10", "Initializing payment providers...")
225+
stripeProvider := providers.CreateStripeProvider(cfg.Stripe.Secret)
226+
xenditProvider := providers.CreateXenditProvider(cfg.Xendit.Secret)
227+
228+
providerSelector := providers.CreateMultiProviderSelector([]providers.PaymentProvider{stripeProvider, xenditProvider}, providerMappingStore)
229+
printSuccess("Payment providers initialized")
230+
printInfo(" • Stripe: Ready for USD, EUR, GBP")
231+
printInfo(" • Xendit: Ready for IDR, SGD, MYR, PHP, THB, VND")
232+
_ = customerStore
233+
_ = paymentMethodStore
234+
230235
printStep("9/10", "Initializing services...")
231236
paymentService := services.CreatePaymentService(paymentRepo, providerSelector)
232237
subscriptionService := services.CreateSubscriptionService(planRepo, subscriptionRepo, providerSelector)

0 commit comments

Comments
 (0)