Skip to content

Commit 8eed583

Browse files
authored
Merge pull request #6 from Dartmind-OpenSource/chore-001
chore: added charge-api service class for charging card and bank
2 parents 74a45f4 + a547f07 commit 8eed583

File tree

7 files changed

+305
-4
lines changed

7 files changed

+305
-4
lines changed

lib/src/config/retry_policy.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import 'dart:math';
33

44
class RetryPolicy {
5-
65
const RetryPolicy({
76
this.maxAttempts = 3,
87
this.baseDelay = 1000,
@@ -20,6 +19,7 @@ class RetryPolicy {
2019
backoffFactor: 1.5,
2120
);
2221
}
22+
2323
/// Maximum number of retry attempts
2424
final int maxAttempts;
2525

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
import 'package:mind_paystack/src/client/mind_paystack_client.dart';
3+
4+
class ChargeApiService {
5+
ChargeApiService({
6+
required this.client,
7+
required this.apiUrl,
8+
});
9+
10+
final MindPaystackClient client;
11+
final String apiUrl;
12+
13+
/// Initiate Charge (Common for both Card and Bank)
14+
Future<Map<String, dynamic>> initiateCharge(Map<String, dynamic> chargeDetails) async {
15+
final response = await client.post<Map<String, dynamic>>(
16+
'$apiUrl/charge',
17+
data: chargeDetails,
18+
);
19+
20+
return response; // Assuming the `response` is already parsed to a Map.
21+
}
22+
23+
/// Charge Using Card
24+
Future<Map<String, dynamic>> chargeCard({
25+
required String email,
26+
required String cardNumber,
27+
required String cvv,
28+
required String expiryMonth,
29+
required String expiryYear,
30+
required double amount,
31+
32+
}) async {
33+
final chargeDetails = {
34+
"email": email,
35+
"amount": (amount * 100).toInt(), // Convert amount to kobo
36+
"card": {
37+
"number": cardNumber,
38+
"cvv": cvv,
39+
"expiry_month": expiryMonth,
40+
"expiry_year": expiryYear,
41+
},
42+
};
43+
44+
return await initiateCharge(chargeDetails);
45+
}
46+
47+
/// Charge Using Bank
48+
Future<Map<String, dynamic>> chargeBank({
49+
required String email,
50+
required String accountNumber,
51+
required String bankCode,
52+
required double amount,
53+
}) async {
54+
final chargeDetails = {
55+
"email": email,
56+
"amount": (amount * 100).toInt(), // Convert amount to kobo
57+
"bank": {
58+
"account_number": accountNumber,
59+
"code": bankCode,
60+
},
61+
};
62+
63+
return await initiateCharge(chargeDetails);
64+
}
65+
}

lib/src/model/payment_model.dart

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import 'dart:convert';
2+
3+
4+
/// Enum defining payment types
5+
enum PaymentType {
6+
/// Card payment type
7+
card,
8+
/// Bank transfer payment type
9+
bank
10+
}
11+
12+
class Card {
13+
final String number;
14+
final String cvv;
15+
final String expiryMonth;
16+
final String expiryYear;
17+
18+
/// Creates a new [Card] instance
19+
Card({
20+
required this.number,
21+
required this.cvv,
22+
required this.expiryMonth,
23+
required this.expiryYear,
24+
});
25+
26+
/// Creates a [Card] instance from JSON map
27+
factory Card.fromJson(Map<String, dynamic> json) {
28+
return Card(
29+
number: json['number']?.toString() ?? '',
30+
cvv: json['cvv']?.toString() ?? '',
31+
expiryMonth: json['expiry_month']?.toString() ?? '',
32+
expiryYear: json['expiry_year']?.toString() ?? '',
33+
);
34+
}
35+
36+
/// Converts the card details to JSON map
37+
Map<String, dynamic> toJson() => {
38+
'number': number,
39+
'cvv': cvv,
40+
'expiry_month': expiryMonth,
41+
'expiry_year': expiryYear,
42+
};
43+
44+
/// Validates the card details
45+
bool isValid() =>
46+
number.isNotEmpty &&
47+
cvv.isNotEmpty &&
48+
expiryMonth.isNotEmpty &&
49+
expiryYear.isNotEmpty;
50+
}
51+
52+
/// Model class representing bank account details
53+
class Bank {
54+
final String accountNumber;
55+
final String code;
56+
57+
/// Creates a new [Bank] instance
58+
Bank({
59+
required this.accountNumber,
60+
required this.code,
61+
});
62+
63+
/// Creates a [Bank] instance from JSON map
64+
factory Bank.fromJson(Map<String, dynamic> json) {
65+
return Bank(
66+
accountNumber: json['account_number']?.toString() ?? '',
67+
code: json['code']?.toString() ?? '',
68+
);
69+
}
70+
71+
/// Converts the bank details to JSON map
72+
Map<String, dynamic> toJson() => {
73+
'account_number': accountNumber,
74+
'code': code,
75+
};
76+
77+
/// Validates the bank details
78+
bool isValid() => accountNumber.isNotEmpty && code.isNotEmpty;
79+
}
80+
81+
/// Abstract class defining common payment request properties
82+
abstract class PaymentRequest {
83+
/// Email address of the customer
84+
String get email;
85+
86+
/// Payment amount in main currency
87+
double get amount;
88+
89+
/// Converts the payment request to JSON map
90+
Map<String, dynamic> toJson();
91+
92+
/// Validates the payment request
93+
bool isValid();
94+
}
95+
96+
/// Implementation of card-based payment request
97+
class CardPaymentRequest implements PaymentRequest {
98+
@override
99+
final String email;
100+
@override
101+
final double amount;
102+
final Card card;
103+
104+
/// Creates a new [CardPaymentRequest] instance
105+
CardPaymentRequest({
106+
required this.email,
107+
required this.amount,
108+
required this.card,
109+
});
110+
111+
/// Gets the amount in kobo (amount * 100)
112+
int get amountInKobo => (amount * 100).toInt();
113+
114+
/// Creates a [CardPaymentRequest] instance from JSON map
115+
factory CardPaymentRequest.fromJson(Map<String, dynamic> json) {
116+
return CardPaymentRequest(
117+
email: json['email']?.toString() ?? '',
118+
amount: (json['amount'] as num?)?.toDouble() ?? 0.0 / 100,
119+
card: Card.fromJson(json['card'] as Map<String, dynamic>),
120+
);
121+
}
122+
123+
@override
124+
/// Converts the card payment request to JSON map
125+
Map<String, dynamic> toJson() => {
126+
'email': email,
127+
'amount': amountInKobo,
128+
'card': card.toJson(),
129+
};
130+
131+
@override
132+
/// Validates the card payment request
133+
bool isValid() =>
134+
email.isNotEmpty &&
135+
amount > 0 &&
136+
card.isValid();
137+
}
138+
139+
/// Implementation of bank-based payment request
140+
class BankPaymentRequest implements PaymentRequest {
141+
@override
142+
final String email;
143+
@override
144+
final double amount;
145+
final Bank bank;
146+
147+
/// Creates a new [BankPaymentRequest] instance
148+
BankPaymentRequest({
149+
required this.email,
150+
required this.amount,
151+
required this.bank,
152+
});
153+
154+
/// Gets the amount in kobo (amount * 100)
155+
int get amountInKobo => (amount * 100).toInt();
156+
157+
/// Creates a [BankPaymentRequest] instance from JSON map
158+
factory BankPaymentRequest.fromJson(Map<String, dynamic> json) {
159+
return BankPaymentRequest(
160+
email: json['email']?.toString() ?? '',
161+
amount: (json['amount'] as num?)?.toDouble() ?? 0.0 / 100,
162+
bank: Bank.fromJson(json['bank'] as Map<String, dynamic>),
163+
);
164+
}
165+
166+
@override
167+
/// Converts the bank payment request to JSON map
168+
Map<String, dynamic> toJson() => {
169+
'email': email,
170+
'amount': amountInKobo,
171+
'bank': bank.toJson(),
172+
};
173+
174+
@override
175+
/// Validates the bank payment request
176+
bool isValid() =>
177+
email.isNotEmpty &&
178+
amount > 0 &&
179+
bank.isValid();
180+
}

lib/src/services/bank_payment_service.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import 'package:mind_paystack/src/client/mind_paystack_client.dart';
22
import 'package:mind_paystack/src/config/retry_policy.dart';
33
import 'package:mind_paystack/src/model/bank_model.dart';
4-
import 'package:mind_paystack/src/ui/app_api_endpoints.dart';
4+
import 'package:mind_paystack/src/utils/app_api_endpoints.dart';
5+
56

67
abstract class BankPaymentService {
78
Future<Map<String, dynamic>> initializePayment({
@@ -29,11 +30,12 @@ class BankPaymentServiceImpl implements BankPaymentService {
2930
final MindPaystackClient _mindPaystackClient;
3031
final RetryPolicy _retryPolicy;
3132

33+
3234
@override
3335
Future<Map<String, dynamic>> initializePayment({
3436
required String accountNumber,
3537
required String bankCode,
36-
required double amount,
38+
required double amount,
3739
required String currency,
3840
String? email,
3941
Map<String, dynamic>? metadata,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import 'package:mind_paystack/src/client/mind_paystack_client.dart';
2+
import 'package:mind_paystack/src/model/payment_model.dart';
3+
4+
abstract class ChargeApiService {
5+
ChargeApiService({
6+
required this.client,
7+
required this.apiUrl,
8+
});
9+
10+
final MindPaystackClient client;
11+
final String apiUrl;
12+
13+
/// Initiate Charge (Common for both Card and Bank)
14+
15+
Future<Map<String, dynamic>> initiateCharge({
16+
CardPaymentRequest? cardDetails,
17+
BankPaymentRequest? bankDetails,
18+
required PaymentType type,
19+
}) async {
20+
// Choose payment details based on type
21+
final paymentDetails = switch (type) {
22+
PaymentType.card => cardDetails?.toJson() ??
23+
(throw ArgumentError('Card details required for card payment')),
24+
PaymentType.bank => bankDetails?.toJson() ??
25+
(throw ArgumentError('Bank details required for bank payment')),
26+
};
27+
28+
final response = await client.post<Map<String, dynamic>>(
29+
'$apiUrl/charge',
30+
data: paymentDetails,
31+
);
32+
return response;
33+
}
34+
35+
/// Charge Using Card
36+
Future<Map<String, dynamic>> chargeCard({
37+
required CardPaymentRequest chargeDetails,
38+
}) async {
39+
return await initiateCharge(
40+
cardDetails: chargeDetails,
41+
type:PaymentType.card,
42+
);
43+
}
44+
45+
/// Charge Using Bank
46+
Future<Map<String, dynamic>> chargeBank({
47+
required BankPaymentRequest bankDetails
48+
}) async {
49+
return await initiateCharge(
50+
bankDetails: bankDetails,
51+
type:PaymentType.bank,
52+
);
53+
}
54+
}

lib/src/services/transaction_service.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'package:mind_paystack/src/client/mind_paystack_client.dart';
22
import 'package:mind_paystack/src/config/retry_policy.dart';
3-
import 'package:mind_paystack/src/ui/app_api_endpoints.dart';
3+
import 'package:mind_paystack/src/utils/app_api_endpoints.dart';
44

55
abstract class TransactionService {
66
Future<Map<String, dynamic>> verifyTransaction(String transactionReference);

test/mind_paystack_test.dart

Whitespace-only changes.

0 commit comments

Comments
 (0)