Skip to content

Commit ae415b1

Browse files
Adding payment splits (#66)
* adding Supported Networks in payment config * adding netwrok utils * update tests * adding new network icon * add network icon validation logic * adding missing imports * update network detection validation logic * allign card number field with network icon * update local * add more validation * update local * refactor * fixing RTL * add expiry validation * fixing RTL card number formatter * update network detection * update network in example * increase version number 2.1.0 * pub get * fixing comments * Fix Apple Pay PaymentNetwork serialization issue - Convert PaymentNetwork enum instances to string values using toJson() method - Fixes 'Invalid argument: Instance of PaymentNetwork' error in Apple Pay - Updated isApplePayAvailable, createConfigString, and createCustomNativeConfig methods - Ensures proper communication between Flutter and iOS native code * Bump version to 2.1.1 and update changelog - Update version from 2.1.0 to 2.1.1 in pubspec.yaml - Add changelog entry for Apple Pay PaymentNetwork serialization fix - Document the bug fix that resolves 'Invalid argument: Instance of PaymentNetwork' error * update version * update version number * adding new fonts * update yml * update ui * update logo * update padding * finalize rebranding * update version * update version number * adding split model * adding new splits to the payment request and resposne * update demo * adding new base url to easely customize it in case of staging or production * update moyasar service to use base url * update to v2.2.0
1 parent 6e8ca28 commit ae415b1

File tree

11 files changed

+163
-52
lines changed

11 files changed

+163
-52
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 2.2.0
2+
3+
Split Payment Feature 🐛
4+
5+
- [Moyasar] Adding Split Payment support.
6+
17
## 2.1.3
28

39
STC UI Customization 🐛

example/lib/main.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,21 @@ class _CoffeeShopState extends State<CoffeeShop> {
3939
merchantId: 'merchant.com.mysr.apple',
4040
label: 'Blue Coffee Beans',
4141
manual: false,
42-
saveCard: false));
42+
saveCard: false),
43+
// splits: [
44+
// PaymentSplit(
45+
// recipientId: "7d2d0797-a2be-40fe-bb1b-1fdec9824c95",
46+
// amount: 8000),
47+
// PaymentSplit(
48+
// recipientId: "327680bb-d790-4643-8e10-31455a1ab3a6",
49+
// amount: 2000,
50+
// reference: "optional-reference-for-split-1fcfcbe9-ba75-4eed",
51+
// description: "Platform processing fee",
52+
// feeSource: true,
53+
// refundable: false
54+
// )
55+
// ]
56+
);
4357

4458
void onPaymentResult(result) {
4559
if (result is PaymentResponse) {

example/pubspec.lock

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -108,26 +108,26 @@ packages:
108108
dependency: transitive
109109
description:
110110
name: leak_tracker
111-
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
111+
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
112112
url: "https://pub.dev"
113113
source: hosted
114-
version: "10.0.9"
114+
version: "11.0.2"
115115
leak_tracker_flutter_testing:
116116
dependency: transitive
117117
description:
118118
name: leak_tracker_flutter_testing
119-
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
119+
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
120120
url: "https://pub.dev"
121121
source: hosted
122-
version: "3.0.9"
122+
version: "3.0.10"
123123
leak_tracker_testing:
124124
dependency: transitive
125125
description:
126126
name: leak_tracker_testing
127-
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
127+
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
128128
url: "https://pub.dev"
129129
source: hosted
130-
version: "3.0.1"
130+
version: "3.0.2"
131131
lints:
132132
dependency: transitive
133133
description:
@@ -156,17 +156,17 @@ packages:
156156
dependency: transitive
157157
description:
158158
name: meta
159-
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
159+
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
160160
url: "https://pub.dev"
161161
source: hosted
162-
version: "1.16.0"
162+
version: "1.17.0"
163163
moyasar:
164164
dependency: "direct main"
165165
description:
166166
path: ".."
167167
relative: true
168168
source: path
169-
version: "2.1.3"
169+
version: "2.2.0"
170170
path:
171171
dependency: transitive
172172
description:
@@ -264,10 +264,10 @@ packages:
264264
dependency: transitive
265265
description:
266266
name: test_api
267-
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
267+
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
268268
url: "https://pub.dev"
269269
source: hosted
270-
version: "0.7.4"
270+
version: "0.7.7"
271271
typed_data:
272272
dependency: transitive
273273
description:
@@ -280,18 +280,18 @@ packages:
280280
dependency: transitive
281281
description:
282282
name: vector_math
283-
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
283+
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
284284
url: "https://pub.dev"
285285
source: hosted
286-
version: "2.1.4"
286+
version: "2.2.0"
287287
vm_service:
288288
dependency: transitive
289289
description:
290290
name: vm_service
291-
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
291+
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
292292
url: "https://pub.dev"
293293
source: hosted
294-
version: "15.0.0"
294+
version: "15.0.2"
295295
web:
296296
dependency: transitive
297297
description:
@@ -304,18 +304,18 @@ packages:
304304
dependency: transitive
305305
description:
306306
name: webview_flutter
307-
sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba
307+
sha256: a3da219916aba44947d3a5478b1927876a09781174b5a2b67fa5be0555154bf9
308308
url: "https://pub.dev"
309309
source: hosted
310-
version: "4.13.0"
310+
version: "4.13.1"
311311
webview_flutter_android:
312312
dependency: transitive
313313
description:
314314
name: webview_flutter_android
315-
sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3"
315+
sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510
316316
url: "https://pub.dev"
317317
source: hosted
318-
version: "4.10.1"
318+
version: "4.10.11"
319319
webview_flutter_platform_interface:
320320
dependency: transitive
321321
description:
@@ -328,10 +328,10 @@ packages:
328328
dependency: transitive
329329
description:
330330
name: webview_flutter_wkwebview
331-
sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f
331+
sha256: e49f378ed066efb13fc36186bbe0bd2425630d4ea0dbc71a18fdd0e4d8ed8ebc
332332
url: "https://pub.dev"
333333
source: hosted
334-
version: "3.23.0"
334+
version: "3.23.5"
335335
yaml:
336336
dependency: transitive
337337
description:
@@ -341,5 +341,5 @@ packages:
341341
source: hosted
342342
version: "3.1.3"
343343
sdks:
344-
dart: ">=3.7.0 <4.0.0"
345-
flutter: ">=3.29.0"
344+
dart: ">=3.9.0 <4.0.0"
345+
flutter: ">=3.35.0"

lib/moyasar.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export 'src/widgets/apple_pay.dart' show ApplePay;
88

99
export 'src/models/card_form_model.dart' show CardFormModel;
1010
export 'src/models/payment_config.dart' show PaymentConfig, PaymentNetwork;
11+
export 'src/models/payment_split.dart' show PaymentSplit;
1112
export 'src/models/apple_pay_config.dart' show ApplePayConfig;
1213
export 'src/models/credit_card_config.dart' show CreditCardConfig;
1314
export 'src/models/payment_request.dart' show PaymentRequest;

lib/src/models/payment_config.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:moyasar/src/models/apple_pay_config.dart';
22
import 'package:moyasar/src/models/credit_card_config.dart';
3+
import 'package:moyasar/src/models/payment_split.dart';
34

45
/// Supported Networks: [PaymentNetwork.amex, PaymentNetwork.visa, PaymentNetwork.mada, PaymentNetwork.masterCard]
56
enum PaymentNetwork {
@@ -61,6 +62,16 @@ class PaymentConfig {
6162
/// given_id It is going be the ID of the created payment.
6263
String? givenID;
6364

65+
/// Optional payment splits for dividing payment amount among recipients.
66+
/// Only available for aggregation clients. Splitting on non-aggregated payments will be ignored.
67+
/// Contact your account manager to enable this feature.
68+
List<PaymentSplit>? splits;
69+
70+
/// Optional base URL for the API endpoint.
71+
/// Default is 'https://api.moyasar.com/v1/payments'.
72+
/// For staging, use 'https://apimig.moyasar.com/v1/payments'.
73+
String? baseUrl;
74+
6475
PaymentConfig(
6576
{required this.publishableApiKey,
6677
required this.amount,
@@ -70,7 +81,9 @@ class PaymentConfig {
7081
List<PaymentNetwork>? supportedNetworks,
7182
this.applePay,
7283
this.creditCard,
73-
this.givenID})
84+
this.givenID,
85+
this.splits,
86+
this.baseUrl})
7487
: supportedNetworks = (supportedNetworks ?? const [
7588
PaymentNetwork.visa,
7689
PaymentNetwork.mada,

lib/src/models/payment_request.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class PaymentRequest {
1212
late PaymentRequestSource source;
1313
String? givenID;
1414
String callbackUrl = PaymentConfig.callbackUrl;
15+
List<PaymentSplit>? splits;
16+
String? baseUrl;
1517

1618
PaymentRequest(
1719
PaymentConfig config, PaymentRequestSource paymentRequestSource) {
@@ -21,6 +23,8 @@ class PaymentRequest {
2123
metadata = config.metadata;
2224
source = paymentRequestSource;
2325
givenID = config.givenID;
26+
splits = config.splits;
27+
baseUrl = config.baseUrl;
2428
}
2529

2630
Map<String, dynamic> toJson() => {
@@ -31,5 +35,7 @@ class PaymentRequest {
3135
'metadata': metadata,
3236
'callback_url': callbackUrl,
3337
if (givenID != null) 'given_id': givenID,
38+
if (splits != null && splits!.isNotEmpty)
39+
'splits': splits!.map((split) => split.toJson()).toList(),
3440
};
3541
}

lib/src/models/payment_response.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:moyasar/moyasar.dart';
22
import 'package:moyasar/src/models/payment_type.dart';
3+
import 'package:moyasar/src/models/payment_split.dart';
34

45
/// Moyasar API response for processing a payment.
56
class PaymentResponse {
@@ -25,6 +26,7 @@ class PaymentResponse {
2526
late String updatedAt;
2627
Map<String, dynamic>? metadata;
2728
late dynamic source;
29+
List<PaymentSplit>? splits;
2830

2931
PaymentResponse.fromJson(Map<String, dynamic> json, PaymentType paymentType) {
3032
id = json['id'];
@@ -52,6 +54,12 @@ class PaymentResponse {
5254
metadata = Map<String, dynamic>.from(json['metadata']);
5355
}
5456

57+
if (json['splits'] != null) {
58+
splits = (json['splits'] as List)
59+
.map((split) => PaymentSplit.fromJson(split as Map<String, dynamic>))
60+
.toList();
61+
}
62+
5563
if (paymentType == PaymentType.creditcard) {
5664
source = CardPaymentResponseSource.fromJson(json['source']);
5765
} else if (paymentType == PaymentType.applepay) {
@@ -88,6 +96,9 @@ class PaymentResponse {
8896
if (metadata != null) {
8997
data['metadata'] = metadata;
9098
}
99+
if (splits != null) {
100+
data['splits'] = splits!.map((split) => split.toJson()).toList();
101+
}
91102
if (source != null) {
92103
if (source is CardPaymentResponseSource) {
93104
data['source'] = (source as CardPaymentResponseSource).toJson();

lib/src/models/payment_split.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/// Represents a payment split for dividing payment amount among recipients.
2+
///
3+
/// Payment splitting allows a merchant or platform to divide the payment amount
4+
/// along multiple recipients programmatically, ensuring compliance with SAMA and ZATCA regulations.
5+
class PaymentSplit {
6+
7+
/// The recipient ID (entity_id, platform_id, or beneficiary_id)
8+
final String recipientId;
9+
10+
/// The amount to be split to this recipient (in smallest currency unit)
11+
final int amount;
12+
13+
/// The type of recipient: "Entity", "Platform", or "Beneficiary"
14+
final String? recipientType;
15+
16+
/// Optional description for this split
17+
final String? description;
18+
19+
/// Optional reference for this split
20+
final String? reference;
21+
22+
/// Whether this split is the fee source (only one split can be fee_source)
23+
final bool feeSource;
24+
25+
/// Whether this split is refundable
26+
final bool refundable;
27+
28+
const PaymentSplit({
29+
required this.recipientId,
30+
required this.amount,
31+
this.recipientType,
32+
this.description,
33+
this.reference,
34+
this.feeSource = false,
35+
this.refundable = true,
36+
});
37+
38+
Map<String, dynamic> toJson() => {
39+
'recipient_type': recipientType,
40+
'recipient_id': recipientId,
41+
'amount': amount,
42+
if (description != null) 'description': description,
43+
if (reference != null) 'reference': reference,
44+
'fee_source': feeSource,
45+
'refundable': refundable,
46+
};
47+
48+
factory PaymentSplit.fromJson(Map<String, dynamic> json) {
49+
return PaymentSplit(
50+
recipientType: json['recipient_type'] as String?,
51+
recipientId: json['recipient_id'] as String,
52+
amount: json['amount'] as int,
53+
description: json['description'] as String?,
54+
reference: json['reference'] as String?,
55+
feeSource: json['fee_source'] as bool? ?? false,
56+
refundable: json['refundable'] as bool? ?? true,
57+
);
58+
}
59+
}

lib/src/moyasar.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:moyasar/moyasar.dart';
44

55
import 'models/payment_type.dart';
66

7-
const version = '2.1.3';
7+
const version = '2.2.0';
88

99
/// Payment service.
1010
///
@@ -16,9 +16,10 @@ class Moyasar {
1616
{required String apiKey, required PaymentRequest paymentRequest}) async {
1717
final headers = buildRequestHeaders(apiKey);
1818
final body = jsonEncode(paymentRequest.toJson());
19+
final url = paymentRequest.baseUrl ?? apiUrl;
1920

2021
final res = await http
21-
.post(Uri.parse(apiUrl), headers: headers, body: body)
22+
.post(Uri.parse(url), headers: headers, body: body)
2223
.onError((error, stackTrace) =>
2324
http.Response(jsonEncode({'type': NetworkError.type}), 400))
2425
.timeout(const Duration(seconds: 45),

0 commit comments

Comments
 (0)