Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.example.airwallex_payment_flutter.util.AirwallexPaymentSessionParser
import com.example.airwallex_payment_flutter.util.AirwallexRecurringSessionParser
import com.example.airwallex_payment_flutter.util.AirwallexRecurringWithIntentSessionParser
import com.example.airwallex_payment_flutter.util.AirwallexSupportedBrandsParser
import com.example.airwallex_payment_flutter.util.PaymentSheetConfigurationParser
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import org.json.JSONObject
Expand Down Expand Up @@ -67,9 +68,12 @@ class AirwallexPaymentSdkModule {
result: MethodChannel.Result
) {
val session = parseSessionFromCall(call)
val configJson = call.arguments<JSONObject>()?.optJSONObject("configuration")
val configuration = PaymentSheetConfigurationParser.parse(configJson)
AirwallexStarter.presentEntirePaymentFlow(
activity = activity,
session = session,
configuration = configuration,
paymentResultListener = object : Airwallex.PaymentResultListener {
override fun onCompleted(status: AirwallexPaymentStatus) {
AirwallexLogger.info("AirwallexPaymentSdkModule: presentEntirePaymentFlow, status = $status")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.airwallex_payment_flutter.util

import com.airwallex.android.core.PaymentMethodsLayoutType
import com.airwallex.android.view.composables.PaymentElementConfiguration
import org.json.JSONObject

object PaymentSheetConfigurationParser {
fun parse(json: JSONObject?): PaymentElementConfiguration.PaymentSheet {
val layout = when (json?.optString("layout")?.lowercase()) {
"accordion" -> PaymentMethodsLayoutType.ACCORDION
else -> PaymentMethodsLayoutType.TAB
}
return PaymentElementConfiguration.PaymentSheet(layout = layout)
}
}
13 changes: 12 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:airwallex_payment_flutter/airwallex.dart';
import 'package:airwallex_payment_flutter/types/environment.dart';
import 'package:airwallex_payment_flutter/types/payment_result.dart';
import 'package:airwallex_payment_flutter/types/payment_session.dart';
import 'package:airwallex_payment_flutter/types/payment_sheet_configuration.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:restart_app/restart_app.dart';
Expand Down Expand Up @@ -267,7 +268,17 @@ class MyHomePageState extends State<MyHomePage> {
onPressed: () => _handleSubmit(() async =>
airwallex.presentEntirePaymentFlow(
await _createSession(customerId: customerId))),
child: const Text('presentEntirePaymentFlow'),
child: const Text('presentEntirePaymentFlow (tab)'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => _handleSubmit(() async =>
airwallex.presentEntirePaymentFlow(
await _createSession(customerId: customerId),
configuration: const PaymentSheetConfiguration(
layout: PaymentLayout.accordion),
)),
child: const Text('presentEntirePaymentFlow (accordion)'),
),
const SizedBox(height: 20),
ElevatedButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class AirwallexPaymentFlutterPlugin: NSObject, FlutterPlugin {
case "presentEntirePaymentFlow":
let arguments = call.arguments as! NSDictionary
let session = arguments["session"] as! NSDictionary
sdk?.presentEntirePaymentFlow(clientSecret: session["clientSecret"] as! String, session: session, result: result)
let configuration = arguments["configuration"] as? NSDictionary
sdk?.presentEntirePaymentFlow(clientSecret: session["clientSecret"] as! String, session: session, configuration: configuration, result: result)
case "presentCardPaymentFlow":
let arguments = call.arguments as! NSDictionary
let session = arguments["session"] as! NSDictionary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ class AirwallexSdk: NSObject {
AnalyticsLogger.shared().bindExtraCommonData(["framework": "flutter", "frameworkVersion": "0.1.14"])
}

func presentEntirePaymentFlow(clientSecret: String, session: NSDictionary, result: @escaping FlutterResult) {
func presentEntirePaymentFlow(clientSecret: String, session: NSDictionary, configuration: NSDictionary?, result: @escaping FlutterResult) {
self.result = result

AWXAPIClientConfiguration.shared().clientSecret = clientSecret

let session = buildAirwallexSession(from: session)

DispatchQueue.main.async {
let config = AWXUIContext.Configuration(from: configuration)
AWXUIContext.launchPayment(
from: self.getViewController(),
session: session,
paymentResultDelegate: self,
launchStyle: .present
configuration: config
)
}
}
Expand Down Expand Up @@ -157,6 +158,18 @@ extension AirwallexSdk: AWXPaymentResultDelegate {
}
}

private extension AWXUIContext.Configuration {
convenience init(from params: NSDictionary?) {
self.init()
self.launchStyle = .present
if let layout = params?["layout"] as? String, layout.lowercased() == "accordion" {
self.layout = .accordion
} else {
self.layout = .tab
}
}
}

private extension AirwallexSDKMode {
static func from(_ stringValue: String) -> Self {
switch stringValue {
Expand Down
8 changes: 6 additions & 2 deletions lib/airwallex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '/types/card.dart';
import '/types/card_brand.dart';
import '/types/payment_result.dart';
import '/types/payment_session.dart';
import '/types/payment_sheet_configuration.dart';

import 'airwallex_payment_flutter_platform_interface.dart';

Expand All @@ -20,9 +21,12 @@ class Airwallex {
AirwallexPaymentFlutterPlatform.instance.initialize(environment, enableLogging, saveLogToLocal);
}

Future<PaymentResult> presentEntirePaymentFlow(BaseSession session) {
Future<PaymentResult> presentEntirePaymentFlow(
BaseSession session, {
PaymentSheetConfiguration? configuration,
}) {
return AirwallexPaymentFlutterPlatform.instance
.presentEntirePaymentFlow(session);
.presentEntirePaymentFlow(session, configuration: configuration);
}

Future<PaymentResult> presentCardPaymentFlow(
Expand Down
11 changes: 9 additions & 2 deletions lib/airwallex_payment_flutter_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'types/card.dart';
import 'types/card_brand.dart';
import 'types/payment_result.dart';
import 'types/payment_session.dart';
import 'types/payment_sheet_configuration.dart';

/// An implementation of [AirwallexPaymentFlutterPlatform] that uses method channels.
class MethodChannelAirwallexPaymentFlutter
Expand All @@ -30,9 +31,15 @@ class MethodChannelAirwallexPaymentFlutter
}

@override
Future<PaymentResult> presentEntirePaymentFlow(BaseSession session) async {
Future<PaymentResult> presentEntirePaymentFlow(
BaseSession session, {
PaymentSheetConfiguration? configuration,
}) async {
final result = await methodChannel.invokeMethod(
'presentEntirePaymentFlow', {'session': session.toJson()});
'presentEntirePaymentFlow', {
'session': session.toJson(),
if (configuration != null) 'configuration': configuration.toJson(),
Comment thread
aw-hector marked this conversation as resolved.
});
return parsePaymentResult(result);
}

Expand Down
6 changes: 5 additions & 1 deletion lib/airwallex_payment_flutter_platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import '/types/card.dart';
import '/types/card_brand.dart';
import '/types/payment_result.dart';
import '/types/payment_session.dart';
import '/types/payment_sheet_configuration.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

import 'airwallex_payment_flutter_method_channel.dart';
Expand Down Expand Up @@ -36,7 +37,10 @@ abstract class AirwallexPaymentFlutterPlatform extends PlatformInterface {
throw UnimplementedError('initialize() has not been implemented.');
}

Future<PaymentResult> presentEntirePaymentFlow(BaseSession session) {
Future<PaymentResult> presentEntirePaymentFlow(
BaseSession session, {
PaymentSheetConfiguration? configuration,
}) {
throw UnimplementedError('presentEntirePaymentFlow() has not been implemented.');
}

Expand Down
9 changes: 9 additions & 0 deletions lib/types/payment_sheet_configuration.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enum PaymentLayout { tab, accordion }

class PaymentSheetConfiguration {
final PaymentLayout layout;

const PaymentSheetConfiguration({this.layout = PaymentLayout.tab});

Map<String, dynamic> toJson() => {'layout': layout.name};
}
77 changes: 76 additions & 1 deletion test/airwallex_payment_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:airwallex_payment_flutter/types/environment.dart';
import 'package:airwallex_payment_flutter/types/payment_consent.dart';
import 'package:airwallex_payment_flutter/types/payment_result.dart';
import 'package:airwallex_payment_flutter/types/payment_session.dart';
import 'package:airwallex_payment_flutter/types/payment_sheet_configuration.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
Expand All @@ -28,7 +29,10 @@ class MockAirwallexPaymentFlutterPlatform
}

@override
Future<PaymentResult> presentEntirePaymentFlow(BaseSession session) {
Future<PaymentResult> presentEntirePaymentFlow(
BaseSession session, {
PaymentSheetConfiguration? configuration,
}) {
throw UnimplementedError();
}

Expand Down Expand Up @@ -68,4 +72,75 @@ void main() {
expect(
initialPlatform, isInstanceOf<MethodChannelAirwallexPaymentFlutter>());
});

group('PaymentSheetConfiguration.toJson', () {
test('default layout serializes to tab', () {
const config = PaymentSheetConfiguration();
expect(config.toJson(), {'layout': 'tab'});
});

test('explicit tab layout serializes to tab', () {
const config = PaymentSheetConfiguration(layout: PaymentLayout.tab);
expect(config.toJson(), {'layout': 'tab'});
});

test('accordion layout serializes to accordion', () {
const config = PaymentSheetConfiguration(layout: PaymentLayout.accordion);
expect(config.toJson(), {'layout': 'accordion'});
});
});

group('MethodChannelAirwallexPaymentFlutter.presentEntirePaymentFlow', () {
TestWidgetsFlutterBinding.ensureInitialized();

final plugin = MethodChannelAirwallexPaymentFlutter();
final channel = plugin.methodChannel;
Map<dynamic, dynamic>? capturedArgs;

OneOffSession buildSession() => OneOffSession(
clientSecret: 'secret',
currency: 'USD',
countryCode: 'US',
amount: 10.0,
paymentIntentId: 'intent_123',
);

setUp(() {
capturedArgs = null;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMessageHandler(channel.name, (message) async {
final call = const JSONMethodCodec().decodeMethodCall(message);
capturedArgs = call.arguments as Map<dynamic, dynamic>;
return const JSONMethodCodec()
.encodeSuccessEnvelope({'status': 'success'});
});
});

tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMessageHandler(channel.name, null);
});

test('omits configuration key when no configuration is passed', () async {
final result = await plugin.presentEntirePaymentFlow(buildSession());

expect(result, isA<PaymentSuccessResult>());
expect(capturedArgs, isNotNull);
expect(capturedArgs!.containsKey('session'), isTrue);
expect(capturedArgs!.containsKey('configuration'), isFalse);
});

test('includes configuration json when accordion configuration is passed',
() async {
final result = await plugin.presentEntirePaymentFlow(
buildSession(),
configuration: const PaymentSheetConfiguration(
layout: PaymentLayout.accordion),
);

expect(result, isA<PaymentSuccessResult>());
expect(capturedArgs, isNotNull);
expect(capturedArgs!['configuration'], {'layout': 'accordion'});
});
});
}
Loading