diff --git a/one_for_all_generator/lib/src/generators/dart_api_builder.dart b/one_for_all_generator/lib/src/generators/dart_api_builder.dart index 5b2c3ec..4fa5e87 100644 --- a/one_for_all_generator/lib/src/generators/dart_api_builder.dart +++ b/one_for_all_generator/lib/src/generators/dart_api_builder.dart @@ -34,6 +34,7 @@ class DartApiBuilder extends ApiBuilder { void _updateHostApiMethod(MethodElement e, MethodBuilder b) { b + ..annotations.add(CodeExpression(Code('override'))) ..returns = Reference('${e.returnType}') ..name = e.name ..requiredParameters.addAll(e.parameters.where((e) => !e.isNamed && e.isRequired).map((e) { @@ -54,6 +55,7 @@ class DartApiBuilder extends ApiBuilder { _library.body.add(Class((b) => b ..name = '_\$${codecs.encodeName(element.name)}' + ..implements.add(Reference(element.name)) ..fields.add(Field((b) => b ..static = true ..modifier = FieldModifier.constant @@ -147,8 +149,8 @@ channel.setMethodCallHandler((call) async { @override void writeSerializableClass(SerializableClassHandler handler, {bool withName = false}) { - final SerializableClassHandler(:element, :flutterToHost, :hostToFlutter, :children) = handler; - final fields = element.fields.where((e) => !e.isStatic && e.isFinal && !e.hasInitializer); + final SerializableClassHandler(:element, :flutterToHost, :hostToFlutter, :params, :children) = + handler; final serializedRef = const Reference('List'); final deserializedRef = Reference(element.name); @@ -184,7 +186,7 @@ channel.setMethodCallHandler((call) async { ..lambda = true ..body = Code('[${[ if (withName) '\'${element.name}\'', - ...fields.map((e) { + ...params.map((e) { return codecs.encodeSerialization(e.type, 'deserialized.${e.name}'); }), ].join(',')}]'))); @@ -198,7 +200,7 @@ channel.setMethodCallHandler((call) async { ..type = serializedRef ..name = 'serialized')) ..lambda = true - ..body = Code('${element.name}(${fields.mapIndexed((i, e) { + ..body = Code('${element.name}(${params.mapIndexed((i, e) { return '${e.name}: ${codecs.encodeDeserialization(e.type, 'serialized[$i]')}'; }).join(',')})'))); } diff --git a/one_for_all_generator/lib/src/generators/kotlin_api_builder.dart b/one_for_all_generator/lib/src/generators/kotlin_api_builder.dart index 090202e..f595190 100644 --- a/one_for_all_generator/lib/src/generators/kotlin_api_builder.dart +++ b/one_for_all_generator/lib/src/generators/kotlin_api_builder.dart @@ -371,8 +371,8 @@ return suspendCoroutine { continuation -> @override void writeSerializableClass(SerializableClassHandler handler, {ClassElement? extend}) { if (!handler.kotlinGeneration) return; - final SerializableClassHandler(:element, :flutterToHost, :hostToFlutter, :children) = handler; - final fields = element.fields.where((e) => !e.isStatic && e.isFinal && !e.hasInitializer); + final SerializableClassHandler(:element, :flutterToHost, :hostToFlutter, :params, :children) = + handler; if (children != null) { _specs.add(KotlinClass( @@ -411,10 +411,10 @@ return suspendCoroutine { continuation -> } _specs.add(KotlinClass( - modifier: fields.isNotEmpty ? KotlinClassModifier.data : null, + modifier: params.isNotEmpty ? KotlinClassModifier.data : null, name: codecs.encodeName(element.name), extend: extend != null ? '${codecs.encodeName(extend.name)}()' : null, - initializers: fields.map((e) { + initializers: params.map((e) { return KotlinField( name: _encodeVarName(e.name), type: codecs.encodeType(e.type), @@ -425,7 +425,7 @@ return suspendCoroutine { continuation -> KotlinMethod( name: 'serialize', returns: 'List', - body: 'return listOf(\n${fields.map((e) { + body: 'return listOf(\n${params.map((e) { return ' ${codecs.encodeSerialization(e.type, _encodeVarName(e.name))},\n'; }).join()})', ), @@ -443,7 +443,7 @@ return suspendCoroutine { continuation -> ), ], returns: codecs.encodeType(element.thisType), - body: 'return ${codecs.encodeName(element.name)}(\n${fields.mapIndexed((i, e) { + body: 'return ${codecs.encodeName(element.name)}(\n${params.mapIndexed((i, e) { return ' ${_encodeVarName(e.name)} = ${codecs.encodeDeserialization(e.type, 'serialized[$i]')},\n'; }).join()})', ), diff --git a/one_for_all_generator/lib/src/generators/swift_api_builder.dart b/one_for_all_generator/lib/src/generators/swift_api_builder.dart index d8be5eb..cd5f9d9 100644 --- a/one_for_all_generator/lib/src/generators/swift_api_builder.dart +++ b/one_for_all_generator/lib/src/generators/swift_api_builder.dart @@ -414,8 +414,8 @@ return try await withCheckedThrowingContinuation { continuation in @override void writeSerializableClass(SerializableClassHandler handler, {ClassElement? extend}) { if (!handler.swiftGeneration) return; - final SerializableClassHandler(:element, :flutterToHost, :hostToFlutter, :children) = handler; - final fields = element.fields.where((e) => !e.isStatic && e.isFinal && !e.hasInitializer); + final SerializableClassHandler(:element, :flutterToHost, :hostToFlutter, :params, :children) = + handler; if (children != null) { _specs.add(SwiftProtocol( @@ -451,7 +451,7 @@ return try await withCheckedThrowingContinuation { continuation in _specs.add(SwiftStruct( name: codecs.encodeName(element.name), implements: [if (extend != null) codecs.encodeName(extend.name)], - fields: fields.map((e) { + fields: params.map((e) { return SwiftField( name: _encodeVarName(e.name), type: codecs.encodeType(e.type), @@ -462,7 +462,7 @@ return try await withCheckedThrowingContinuation { continuation in SwiftMethod( name: 'serialize', returns: '[Any?]', - body: 'return [\n${fields.map((e) { + body: 'return [\n${params.map((e) { return ' ${codecs.encodeSerialization(e.type, _encodeVarName(e.name))},\n'; }).join()}]', ), @@ -478,7 +478,7 @@ return try await withCheckedThrowingContinuation { continuation in ), ], returns: codecs.encodeType(element.thisType), - body: 'return ${codecs.encodeName(element.name)}(\n${fields.mapIndexed((i, e) { + body: 'return ${codecs.encodeName(element.name)}(\n${params.mapIndexed((i, e) { return ' ${_encodeVarName(e.name)}: ${codecs.encodeDeserialization(e.type, 'serialized[$i]')}'; }).join(',\n')}\n)', ), diff --git a/one_for_all_generator/lib/src/handlers.dart b/one_for_all_generator/lib/src/handlers.dart index 8ad0c67..f025088 100644 --- a/one_for_all_generator/lib/src/handlers.dart +++ b/one_for_all_generator/lib/src/handlers.dart @@ -1,4 +1,6 @@ import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; import 'package:one_for_all/one_for_all.dart'; import 'package:one_for_all_generator/one_for_all_generator.dart'; @@ -131,7 +133,7 @@ class SerializableClassHandler extends SerializableHandler { final List? children; - const SerializableClassHandler({ + SerializableClassHandler({ required super.element, required super.kotlinGeneration, required super.swiftGeneration, @@ -163,6 +165,13 @@ class SerializableClassHandler extends SerializableHandler { ); } + late final List params = (flutterToHost + ? element.fields + .where((e) => !e.isStatic && e.isFinal && !e.hasInitializer) + .map(SerializableParamHandler.fromField) + : element.unnamedConstructor!.parameters.map(SerializableParamHandler.fromParameter)) + .sortedBy((e) => e.name); + @override SerializableClassHandler apply({ bool flutterToHost = false, @@ -181,6 +190,22 @@ class SerializableClassHandler extends SerializableHandler { } } +class SerializableParamHandler { + final DartType type; + final String name; + final String? defaultValueCode; + + SerializableParamHandler.fromParameter(ParameterElement element) + : type = element.type, + name = element.name, + defaultValueCode = element.defaultValueCode; + + SerializableParamHandler.fromField(FieldElement element) + : type = element.type, + name = element.name, + defaultValueCode = null; +} + class SerializableEnumHandler extends SerializableHandler { static const _annotationChecker = TypeChecker.fromRuntime(SerializableEnum); diff --git a/stripe_terminal/CHANGELOG.md b/stripe_terminal/CHANGELOG.md index a9b18d5..f6b5f47 100644 --- a/stripe_terminal/CHANGELOG.md +++ b/stripe_terminal/CHANGELOG.md @@ -1,3 +1,6 @@ + +## 3.2.0 +- feat: Added to `Terminal.setSimulatorConfiguration` method - chore(android): Replaced `compileSdkVersion` with `compileSdk` and bumped version to `34` - chore(android): Bumped tools gradle version to `8.1.3` - feat: Bumped [Android](https://github.com/stripe/stripe-terminal-android/blob/master/CHANGELOG.md#320---2023-11-15) diff --git a/stripe_terminal/README.md b/stripe_terminal/README.md index 2e4f5f7..82ea514 100644 --- a/stripe_terminal/README.md +++ b/stripe_terminal/README.md @@ -12,8 +12,8 @@ more simply by supporting streams instead of callbacks for listeners ## Features All features of android and ios sdk are supported (Also the TapToPay feature) -- [Android sdk](https://github.com/stripe/stripe-terminal-android) version: 3.1.0 -- [IOS sdk](https://github.com/stripe/stripe-terminal-ios) version: 3.1.0 +- [Android sdk](https://github.com/stripe/stripe-terminal-android) version: 3.2.0 +- [IOS sdk](https://github.com/stripe/stripe-terminal-ios) version: 3.2.0 > Offline mode is not supported diff --git a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/TerminalPlugin.kt b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/TerminalPlugin.kt index fee282a..0d22a3d 100644 --- a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/TerminalPlugin.kt +++ b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/TerminalPlugin.kt @@ -48,6 +48,7 @@ import mek.stripeterminal.api.RefundApi import mek.stripeterminal.api.Result import mek.stripeterminal.api.SetupIntentApi import mek.stripeterminal.api.SetupIntentUsageApi +import mek.stripeterminal.api.SimulatorConfigurationApi import mek.stripeterminal.api.TerminalExceptionCodeApi import mek.stripeterminal.api.TerminalHandlersApi import mek.stripeterminal.api.TerminalPlatformApi @@ -295,6 +296,10 @@ class TerminalPlugin : FlutterPlugin, ActivityAware, TerminalPlatformApi { }, ) } + + override fun onSetSimulatorConfiguration(configuration: SimulatorConfigurationApi) { + Terminal.getInstance().simulatorConfiguration = configuration.toHost() + } // endregion // region Taking Payment @@ -402,8 +407,8 @@ class TerminalPlugin : FlutterPlugin, ActivityAware, TerminalPlatformApi { } override fun onSuccess(paymentIntent: PaymentIntent) { - result.success(paymentIntent.toApi()) paymentIntents.remove(paymentIntent.id) + result.success(paymentIntent.toApi()) } }, ) diff --git a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/api/TerminalApi.kt b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/api/TerminalApi.kt index 53385e2..b8f1f9b 100644 --- a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/api/TerminalApi.kt +++ b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/api/TerminalApi.kt @@ -101,6 +101,8 @@ interface TerminalPlatformApi { fun onDisconnectReader(result: Result) + fun onSetSimulatorConfiguration(configuration: SimulatorConfigurationApi) + fun onGetPaymentStatus(): PaymentStatusApi fun onCreatePaymentIntent( @@ -230,7 +232,9 @@ interface TerminalPlatformApi { "supportsReadersOfType" -> { val res = onSupportsReadersOfType( - (args[0] as Int?)?.let { DeviceTypeApi.values()[it] }, + (args[0] as Int?)?.let { + DeviceTypeApi.values()[it] + }, (args[1] as List).let { DiscoveryConfigurationApi.deserialize(it) }, ) result.success(res) @@ -265,12 +269,7 @@ interface TerminalPlatformApi { } "listLocations" -> { val res = Result>(result) { it.map { it.serialize() } } - onListLocations( - res, - args[0] as String?, - (args[1] as? Number)?.toLong(), - args[2] as String?, - ) + onListLocations(res, args[0] as String?, (args[1] as? Number)?.toLong(), args[2] as String?) } "installAvailableUpdate" -> { onInstallAvailableUpdate() @@ -284,16 +283,17 @@ interface TerminalPlatformApi { val res = Result(result) { null } onDisconnectReader(res) } + "setSimulatorConfiguration" -> { + onSetSimulatorConfiguration((args[0] as List).let { SimulatorConfigurationApi.deserialize(it) }) + result.success(null) + } "getPaymentStatus" -> { val res = onGetPaymentStatus() result.success(res.ordinal) } "createPaymentIntent" -> { val res = Result(result) { it.serialize() } - onCreatePaymentIntent( - res, - (args[0] as List).let { PaymentIntentParametersApi.deserialize(it) }, - ) + onCreatePaymentIntent(res, (args[0] as List).let { PaymentIntentParametersApi.deserialize(it) }) } "retrievePaymentIntent" -> { val res = Result(result) { it.serialize() } @@ -306,7 +306,9 @@ interface TerminalPlatformApi { (args[0] as Number).toLong(), args[1] as String, args[2] as Boolean, - (args[3] as List?)?.let { TippingConfigurationApi.deserialize(it) }, + (args[3] as List?)?.let { + TippingConfigurationApi.deserialize(it) + }, args[4] as Boolean, args[5] as Boolean, ) @@ -330,9 +332,10 @@ interface TerminalPlatformApi { args[0] as String?, args[1]?.let { hashMapOf( - *(it as HashMap<*, *>) - .map { (k, v) -> k as String to v as String } - .toTypedArray(), + *(it as HashMap<*, *>).map { + (k, v) -> + k as String to v as String + }.toTypedArray(), ) }, args[2] as String?, @@ -369,21 +372,16 @@ interface TerminalPlatformApi { "startCollectRefundPaymentMethod" -> { val res = Result(result) { null } onStartCollectRefundPaymentMethod( - res, - (args[0] as Number).toLong(), - args[1] as String, - (args[2] as Number).toLong(), - args[3] as String, + res, (args[0] as Number).toLong(), args[1] as String, (args[2] as Number).toLong(), args[3] as String, args[4]?.let { hashMapOf( - *(it as HashMap<*, *>) - .map { (k, v) -> k as String to v as String } - .toTypedArray(), + *(it as HashMap<*, *>).map { + (k, v) -> + k as String to v as String + }.toTypedArray(), ) }, - args[5] as Boolean?, - args[6] as Boolean?, - args[7] as Boolean, + args[5] as Boolean?, args[6] as Boolean?, args[7] as Boolean, ) } "stopCollectRefundPaymentMethod" -> { @@ -432,8 +430,7 @@ interface TerminalPlatformApi { class DiscoverReadersControllerApi( binaryMessenger: BinaryMessenger, ) { - private val channel: EventChannel = - EventChannel(binaryMessenger, "mek_stripe_terminal#TerminalPlatform#discoverReaders") + private val channel: EventChannel = EventChannel(binaryMessenger, "mek_stripe_terminal#TerminalPlatform#discoverReaders") fun setHandler( onListen: (sink: ControllerSink>, configuration: DiscoveryConfigurationApi) -> Unit, @@ -447,10 +444,7 @@ class DiscoverReadersControllerApi( ) { val args = arguments as List val sink = ControllerSink>(events) { it.map { it.serialize() } } - onListen( - sink, - (args[0] as List).let { DiscoveryConfigurationApi.deserialize(it) }, - ) + onListen(sink, (args[0] as List).let { DiscoveryConfigurationApi.deserialize(it) }) } override fun onCancel(arguments: Any?) = onCancel() @@ -464,8 +458,7 @@ class DiscoverReadersControllerApi( class TerminalHandlersApi( binaryMessenger: BinaryMessenger, ) { - private val channel: MethodChannel = - MethodChannel(binaryMessenger, "mek_stripe_terminal#TerminalHandlers") + private val channel: MethodChannel = MethodChannel(binaryMessenger, "mek_stripe_terminal#TerminalHandlers") fun requestConnectionToken( onError: (error: PlatformError) -> Unit, @@ -517,10 +510,7 @@ class TerminalHandlersApi( batteryStatus: BatteryStatusApi?, isCharging: Boolean, ) { - channel.invokeMethod( - "_onReaderBatteryLevelUpdate", - listOf(batteryLevel, batteryStatus?.ordinal, isCharging), - ) + channel.invokeMethod("_onReaderBatteryLevelUpdate", listOf(batteryLevel, batteryStatus?.ordinal, isCharging)) } fun readerReportLowBatteryWarning() { @@ -543,10 +533,7 @@ class TerminalHandlersApi( update: ReaderSoftwareUpdateApi?, exception: TerminalExceptionApi?, ) { - channel.invokeMethod( - "_onReaderFinishInstallingUpdate", - listOf(update?.serialize(), exception?.serialize()), - ) + channel.invokeMethod("_onReaderFinishInstallingUpdate", listOf(update?.serialize(), exception?.serialize())) } fun readerReconnectFailed(reader: ReaderApi) { @@ -659,30 +646,30 @@ enum class CardPresentCaptureMethodApi { data class CardPresentDetailsApi( val brand: CardBrandApi?, + val cardholderName: String?, val country: String?, + val emvAuthData: String?, val expMonth: Long, val expYear: Long, val funding: CardFundingTypeApi?, - val last4: String?, - val cardholderName: String?, - val emvAuthData: String?, val generatedCard: String?, val incrementalAuthorizationStatus: IncrementalAuthorizationStatusApi?, + val last4: String?, val networks: CardNetworksApi?, val receipt: ReceiptDetailsApi?, ) { fun serialize(): List { return listOf( brand?.ordinal, + cardholderName, country, + emvAuthData, expMonth, expYear, funding?.ordinal, - last4, - cardholderName, - emvAuthData, generatedCard, incrementalAuthorizationStatus?.ordinal, + last4, networks?.serialize(), receipt?.serialize(), ) @@ -714,36 +701,33 @@ enum class CardPresentRoutingApi { data class CartApi( val currency: String, + val lineItems: List, val tax: Long, val total: Long, - val lineItems: List, ) { companion object { fun deserialize(serialized: List): CartApi { return CartApi( currency = serialized[0] as String, - tax = (serialized[1] as Number).toLong(), - total = (serialized[2] as Number).toLong(), - lineItems = - (serialized[3] as List<*>).map { - (it as List).let { CartLineItemApi.deserialize(it) } - }, + lineItems = (serialized[1] as List<*>).map { (it as List).let { CartLineItemApi.deserialize(it) } }, + tax = (serialized[2] as Number).toLong(), + total = (serialized[3] as Number).toLong(), ) } } } data class CartLineItemApi( + val amount: Long, val description: String, val quantity: Long, - val amount: Long, ) { companion object { fun deserialize(serialized: List): CartLineItemApi { return CartLineItemApi( - description = serialized[0] as String, - quantity = (serialized[1] as Number).toLong(), - amount = (serialized[2] as Number).toLong(), + amount = (serialized[0] as Number).toLong(), + description = serialized[1] as String, + quantity = (serialized[2] as Number).toLong(), ) } } @@ -751,28 +735,28 @@ data class CartLineItemApi( data class ChargeApi( val amount: Long, + val authorizationCode: String?, + val calculatedStatementDescriptor: String?, val currency: String, - val status: ChargeStatusApi, - val paymentMethodDetails: PaymentMethodDetailsApi?, - val description: String, + val description: String?, val id: String, val metadata: HashMap, + val paymentMethodDetails: PaymentMethodDetailsApi?, val statementDescriptorSuffix: String?, - val calculatedStatementDescriptor: String?, - val authorizationCode: String?, + val status: ChargeStatusApi, ) { fun serialize(): List { return listOf( amount, + authorizationCode, + calculatedStatementDescriptor, currency, - status.ordinal, - paymentMethodDetails?.serialize(), description, id, hashMapOf(*metadata.map { (k, v) -> k to v }.toTypedArray()), + paymentMethodDetails?.serialize(), statementDescriptorSuffix, - calculatedStatementDescriptor, - authorizationCode, + status.ordinal, ) } } @@ -815,16 +799,11 @@ sealed class DiscoveryConfigurationApi { companion object { fun deserialize(serialized: List): DiscoveryConfigurationApi { return when (serialized[0]) { - "BluetoothDiscoveryConfiguration" -> - BluetoothDiscoveryConfigurationApi.deserialize(serialized.drop(1)) - "BluetoothProximityDiscoveryConfiguration" -> - BluetoothProximityDiscoveryConfigurationApi.deserialize(serialized.drop(1)) - "HandoffDiscoveryConfiguration" -> - HandoffDiscoveryConfigurationApi.deserialize(serialized.drop(1)) - "InternetDiscoveryConfiguration" -> - InternetDiscoveryConfigurationApi.deserialize(serialized.drop(1)) - "LocalMobileDiscoveryConfiguration" -> - LocalMobileDiscoveryConfigurationApi.deserialize(serialized.drop(1)) + "BluetoothDiscoveryConfiguration" -> BluetoothDiscoveryConfigurationApi.deserialize(serialized.drop(1)) + "BluetoothProximityDiscoveryConfiguration" -> BluetoothProximityDiscoveryConfigurationApi.deserialize(serialized.drop(1)) + "HandoffDiscoveryConfiguration" -> HandoffDiscoveryConfigurationApi.deserialize(serialized.drop(1)) + "InternetDiscoveryConfiguration" -> InternetDiscoveryConfigurationApi.deserialize(serialized.drop(1)) + "LocalMobileDiscoveryConfiguration" -> LocalMobileDiscoveryConfigurationApi.deserialize(serialized.drop(1)) "UsbDiscoveryConfiguration" -> UsbDiscoveryConfigurationApi.deserialize(serialized.drop(1)) else -> throw Error() } @@ -935,68 +914,68 @@ enum class LocationStatusApi { } data class PaymentIntentApi( - val id: String, - val created: Long, - val status: PaymentIntentStatusApi, val amount: Double, - val captureMethod: CaptureMethodApi, - val currency: String, - val metadata: HashMap, - val charges: List, - val paymentMethod: PaymentMethodApi?, - val paymentMethodId: String?, - val amountDetails: AmountDetailsApi?, - val amountTip: Double?, - val statementDescriptor: String?, - val statementDescriptorSuffix: String?, val amountCapturable: Double?, + val amountDetails: AmountDetailsApi?, val amountReceived: Double?, - val applicationId: String?, + val amountTip: Double?, val applicationFeeAmount: Double?, - val cancellationReason: String?, + val applicationId: String?, val canceledAt: Long?, + val cancellationReason: String?, + val captureMethod: CaptureMethodApi, + val charges: List, val clientSecret: String?, val confirmationMethod: ConfirmationMethodApi?, + val created: Long, + val currency: String, val customerId: String?, val description: String?, + val id: String, val invoiceId: String?, + val metadata: HashMap, val onBehalfOf: String?, - val reviewId: String?, + val paymentMethod: PaymentMethodApi?, + val paymentMethodId: String?, val receiptEmail: String?, + val reviewId: String?, val setupFutureUsage: PaymentIntentUsageApi?, + val statementDescriptor: String?, + val statementDescriptorSuffix: String?, + val status: PaymentIntentStatusApi, val transferGroup: String?, ) { fun serialize(): List { return listOf( - id, - created, - status.ordinal, amount, - captureMethod.ordinal, - currency, - hashMapOf(*metadata.map { (k, v) -> k to v }.toTypedArray()), - charges.map { it.serialize() }, - paymentMethod?.serialize(), - paymentMethodId, - amountDetails?.serialize(), - amountTip, - statementDescriptor, - statementDescriptorSuffix, amountCapturable, + amountDetails?.serialize(), amountReceived, - applicationId, + amountTip, applicationFeeAmount, - cancellationReason, + applicationId, canceledAt, + cancellationReason, + captureMethod.ordinal, + charges.map { it.serialize() }, clientSecret, confirmationMethod?.ordinal, + created, + currency, customerId, description, + id, invoiceId, + hashMapOf(*metadata.map { (k, v) -> k to v }.toTypedArray()), onBehalfOf, - reviewId, + paymentMethod?.serialize(), + paymentMethodId, receiptEmail, + reviewId, setupFutureUsage?.ordinal, + statementDescriptor, + statementDescriptorSuffix, + status.ordinal, transferGroup, ) } @@ -1004,52 +983,41 @@ data class PaymentIntentApi( data class PaymentIntentParametersApi( val amount: Long, - val currency: String, + val applicationFeeAmount: Long?, val captureMethod: CaptureMethodApi, - val paymentMethodTypes: List, - val metadata: HashMap, + val currency: String, + val customerId: String?, val description: String?, + val metadata: HashMap, + val onBehalfOf: String?, + val paymentMethodOptionsParameters: PaymentMethodOptionsParametersApi?, + val paymentMethodTypes: List, + val receiptEmail: String?, + val setupFutureUsage: PaymentIntentUsageApi?, val statementDescriptor: String?, val statementDescriptorSuffix: String?, - val receiptEmail: String?, - val customerId: String?, - val applicationFeeAmount: Long?, val transferDataDestination: String?, val transferGroup: String?, - val onBehalfOf: String?, - val setupFutureUsage: PaymentIntentUsageApi?, - val paymentMethodOptionsParameters: PaymentMethodOptionsParametersApi?, ) { companion object { fun deserialize(serialized: List): PaymentIntentParametersApi { return PaymentIntentParametersApi( amount = (serialized[0] as Number).toLong(), - currency = serialized[1] as String, + applicationFeeAmount = (serialized[1] as? Number)?.toLong(), captureMethod = (serialized[2] as Int).let { CaptureMethodApi.values()[it] }, - paymentMethodTypes = - (serialized[3] as List<*>).map { - (it as Int).let { PaymentMethodTypeApi.values()[it] } - }, - metadata = - hashMapOf( - *(serialized[4] as HashMap<*, *>) - .map { (k, v) -> k as String to v as String } - .toTypedArray(), - ), + currency = serialized[3] as String, + customerId = serialized[4] as String?, description = serialized[5] as String?, - statementDescriptor = serialized[6] as String?, - statementDescriptorSuffix = serialized[7] as String?, - receiptEmail = serialized[8] as String?, - customerId = serialized[9] as String?, - applicationFeeAmount = (serialized[10] as? Number)?.toLong(), - transferDataDestination = serialized[11] as String?, - transferGroup = serialized[12] as String?, - onBehalfOf = serialized[13] as String?, - setupFutureUsage = (serialized[14] as Int?)?.let { PaymentIntentUsageApi.values()[it] }, - paymentMethodOptionsParameters = - (serialized[15] as List?)?.let { - PaymentMethodOptionsParametersApi.deserialize(it) - }, + metadata = hashMapOf(*(serialized[6] as HashMap<*, *>).map { (k, v) -> k as String to v as String }.toTypedArray()), + onBehalfOf = serialized[7] as String?, + paymentMethodOptionsParameters = (serialized[8] as List?)?.let { PaymentMethodOptionsParametersApi.deserialize(it) }, + paymentMethodTypes = (serialized[9] as List<*>).map { (it as Int).let { PaymentMethodTypeApi.values()[it] } }, + receiptEmail = serialized[10] as String?, + setupFutureUsage = (serialized[11] as Int?)?.let { PaymentIntentUsageApi.values()[it] }, + statementDescriptor = serialized[12] as String?, + statementDescriptorSuffix = serialized[13] as String?, + transferDataDestination = serialized[14] as String?, + transferGroup = serialized[15] as String?, ) } } @@ -1071,20 +1039,20 @@ enum class PaymentIntentUsageApi { } data class PaymentMethodApi( - val id: String, val card: CardDetailsApi?, val cardPresent: CardPresentDetailsApi?, - val interacPresent: CardPresentDetailsApi?, val customerId: String?, + val id: String, + val interacPresent: CardPresentDetailsApi?, val metadata: HashMap, ) { fun serialize(): List { return listOf( - id, card?.serialize(), cardPresent?.serialize(), - interacPresent?.serialize(), customerId, + id, + interacPresent?.serialize(), hashMapOf(*metadata.map { (k, v) -> k to v }.toTypedArray()), ) } @@ -1108,8 +1076,7 @@ data class PaymentMethodOptionsParametersApi( companion object { fun deserialize(serialized: List): PaymentMethodOptionsParametersApi { return PaymentMethodOptionsParametersApi( - cardPresentParameters = - (serialized[0] as List).let { CardPresentParametersApi.deserialize(it) }, + cardPresentParameters = (serialized[0] as List).let { CardPresentParametersApi.deserialize(it) }, ) } } @@ -1129,27 +1096,27 @@ enum class PaymentStatusApi { } data class ReaderApi( - val locationStatus: LocationStatusApi?, - val deviceType: DeviceTypeApi?, - val simulated: Boolean, - val locationId: String?, - val location: LocationApi?, - val serialNumber: String, val availableUpdate: Boolean, val batteryLevel: Double, + val deviceType: DeviceTypeApi?, val label: String?, + val location: LocationApi?, + val locationId: String?, + val locationStatus: LocationStatusApi?, + val serialNumber: String, + val simulated: Boolean, ) { fun serialize(): List { return listOf( - locationStatus?.ordinal, - deviceType?.ordinal, - simulated, - locationId, - location?.serialize(), - serialNumber, availableUpdate, batteryLevel, + deviceType?.ordinal, label, + location?.serialize(), + locationId, + locationStatus?.ordinal, + serialNumber, + simulated, ) } } @@ -1203,52 +1170,52 @@ data class ReaderSoftwareUpdateApi( data class ReceiptDetailsApi( val accountType: String?, - val applicationPreferredName: String, + val applicationCryptogram: String?, + val applicationPreferredName: String?, val authorizationCode: String?, val authorizationResponseCode: String, - val applicationCryptogram: String, - val dedicatedFileName: String, - val transactionStatusInformation: String, - val terminalVerificationResults: String, + val dedicatedFileName: String?, + val terminalVerificationResults: String?, + val transactionStatusInformation: String?, ) { fun serialize(): List { return listOf( accountType, + applicationCryptogram, applicationPreferredName, authorizationCode, authorizationResponseCode, - applicationCryptogram, dedicatedFileName, - transactionStatusInformation, terminalVerificationResults, + transactionStatusInformation, ) } } data class RefundApi( - val id: String, val amount: Long, val chargeId: String, val created: Long, val currency: String, + val failureReason: String?, + val id: String, val metadata: HashMap, + val paymentMethodDetails: PaymentMethodDetailsApi?, val reason: String?, val status: RefundStatusApi?, - val paymentMethodDetails: PaymentMethodDetailsApi?, - val failureReason: String?, ) { fun serialize(): List { return listOf( - id, amount, chargeId, created, currency, + failureReason, + id, hashMapOf(*metadata.map { (k, v) -> k to v }.toTypedArray()), + paymentMethodDetails?.serialize(), reason, status?.ordinal, - paymentMethodDetails?.serialize(), - failureReason, ) } } @@ -1260,25 +1227,25 @@ enum class RefundStatusApi { } data class SetupAttemptApi( - val id: String, val applicationId: String?, val created: Long, val customerId: String?, + val id: String, val onBehalfOf: String?, - val paymentMethodId: String?, val paymentMethodDetails: SetupAttemptPaymentMethodDetailsApi?, + val paymentMethodId: String?, val setupIntentId: String, val status: SetupAttemptStatusApi, ) { fun serialize(): List { return listOf( - id, applicationId, created, customerId, + id, onBehalfOf, - paymentMethodId, paymentMethodDetails?.serialize(), + paymentMethodId, setupIntentId, status.ordinal, ) @@ -1319,23 +1286,23 @@ enum class SetupAttemptStatusApi { } data class SetupIntentApi( - val id: String, val created: Long, val customerId: String?, + val id: String, + val latestAttempt: SetupAttemptApi?, val metadata: HashMap, - val usage: SetupIntentUsageApi, val status: SetupIntentStatusApi, - val latestAttempt: SetupAttemptApi?, + val usage: SetupIntentUsageApi, ) { fun serialize(): List { return listOf( - id, created, customerId, + id, + latestAttempt?.serialize(), hashMapOf(*metadata.map { (k, v) -> k to v }.toTypedArray()), - usage.ordinal, status.ordinal, - latestAttempt?.serialize(), + usage.ordinal, ) } } @@ -1354,20 +1321,88 @@ enum class SetupIntentUsageApi { OFF_SESSION, } +enum class SimulateReaderUpdateApi { + AVAILABLE, + NONE, + REQUIRED, + RANDOM, +} + +data class SimulatedCardApi( + val testCardNumber: String?, + val type: SimulatedCardTypeApi?, +) { + companion object { + fun deserialize(serialized: List): SimulatedCardApi { + return SimulatedCardApi( + testCardNumber = serialized[0] as String?, + type = (serialized[1] as Int?)?.let { SimulatedCardTypeApi.values()[it] }, + ) + } + } +} + +enum class SimulatedCardTypeApi { + VISA, + VISA_DEBIT, + VISA_US_COMMON_DEBIT, + MASTERCARD, + MASTER_DEBIT, + MASTERCARD_PREPAID, + AMEX, + AMEX2, + DISCOVER, + DISCOVER2, + DINERS, + DINERS14_DIGIT, + JBC, + UNION_PAY, + INTERAC, + EFTPOS_AU_DEBIT, + EFTPOS_AU_VISA_DEBIT, + EFTPOS_AU_DEBIT_MASTERCARD, + CHARGE_DECLINED, + CHARGE_DECLINED_INSUFFICIENT_FUNDS, + CHARGE_DECLINED_LOST_CARD, + CHARGE_DECLINED_STOLEN_CARD, + CHARGE_DECLINED_EXPIRED_CARD, + CHARGE_DECLINED_PROCESSING_ERROR, + ONLINE_PIN_CVM, + ONLINE_PIN_SCA_RETRY, + OFFLINE_PIN_CVM, + OFFLINE_PIN_SCA_RETRY, +} + +data class SimulatorConfigurationApi( + val simulatedCard: SimulatedCardApi, + val simulatedTipAmount: Long?, + val update: SimulateReaderUpdateApi, +) { + companion object { + fun deserialize(serialized: List): SimulatorConfigurationApi { + return SimulatorConfigurationApi( + simulatedCard = (serialized[0] as List).let { SimulatedCardApi.deserialize(it) }, + simulatedTipAmount = (serialized[1] as? Number)?.toLong(), + update = (serialized[2] as Int).let { SimulateReaderUpdateApi.values()[it] }, + ) + } + } +} + data class TerminalExceptionApi( + val apiError: Any?, val code: TerminalExceptionCodeApi, val message: String, - val stackTrace: String?, val paymentIntent: PaymentIntentApi?, - val apiError: Any?, + val stackTrace: String?, ) { fun serialize(): List { return listOf( + apiError, code.ordinal, message, - stackTrace, paymentIntent?.serialize(), - apiError, + stackTrace, ) } } diff --git a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/CardMappings.kt b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/CardMappings.kt index 54ba9d6..83a1311 100644 --- a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/CardMappings.kt +++ b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/CardMappings.kt @@ -75,13 +75,13 @@ fun CardPresentDetails.toApi(): CardPresentDetailsApi { fun ReceiptDetails.toApi(): ReceiptDetailsApi { return ReceiptDetailsApi( accountType = accountType, - applicationPreferredName = applicationPreferredName!!, + applicationPreferredName = applicationPreferredName, authorizationCode = authorizationCode, authorizationResponseCode = authorizationResponseCode!!, - applicationCryptogram = applicationCryptogram!!, - dedicatedFileName = dedicatedFileName!!, - transactionStatusInformation = tsi!!, - terminalVerificationResults = tvr!!, + applicationCryptogram = applicationCryptogram, + dedicatedFileName = dedicatedFileName, + transactionStatusInformation = tsi, + terminalVerificationResults = tvr, ) } diff --git a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/ChargeMappings.kt b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/ChargeMappings.kt index 002dffe..2fc54ea 100644 --- a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/ChargeMappings.kt +++ b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/ChargeMappings.kt @@ -11,26 +11,25 @@ fun Charge.toApi(): ChargeApi { return ChargeApi( amount = amount, currency = currency!!, - status = - when (status) { - "pending" -> ChargeStatusApi.PENDING - "failed" -> ChargeStatusApi.FAILED - "succeeded" -> ChargeStatusApi.SUCCEEDED - else -> throw Error("Unsupported $status") - }, + status = when (status) { + "pending" -> ChargeStatusApi.PENDING + "failed" -> ChargeStatusApi.FAILED + "succeeded" -> ChargeStatusApi.SUCCEEDED + else -> throw Error("Unsupported $status") + }, paymentMethodDetails = paymentMethodDetails?.toApi(), - description = description!!, + description = description, id = id, metadata = metadata?.toHashMap() ?: hashMapOf(), statementDescriptorSuffix = statementDescriptorSuffix, calculatedStatementDescriptor = calculatedStatementDescriptor, - authorizationCode = authorizationCode, + authorizationCode = authorizationCode ) } fun PaymentMethodDetails.toApi(): PaymentMethodDetailsApi { return PaymentMethodDetailsApi( cardPresent = cardPresentDetails?.toApi(), - interacPresent = interacPresentDetails?.toApi(), + interacPresent = interacPresentDetails?.toApi() ) } diff --git a/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/SimulatorConfigurationMappings.kt b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/SimulatorConfigurationMappings.kt new file mode 100644 index 0000000..546182a --- /dev/null +++ b/stripe_terminal/android/src/main/kotlin/mek/stripeterminal/mappings/SimulatorConfigurationMappings.kt @@ -0,0 +1,65 @@ +package mek.stripeterminal.mappings + +import com.stripe.stripeterminal.external.models.SimulateReaderUpdate +import com.stripe.stripeterminal.external.models.SimulatedCard +import com.stripe.stripeterminal.external.models.SimulatedCardType +import com.stripe.stripeterminal.external.models.SimulatorConfiguration +import mek.stripeterminal.api.SimulateReaderUpdateApi +import mek.stripeterminal.api.SimulatedCardApi +import mek.stripeterminal.api.SimulatedCardTypeApi +import mek.stripeterminal.api.SimulatorConfigurationApi + +fun SimulatorConfigurationApi.toHost(): SimulatorConfiguration { + return SimulatorConfiguration( + update = update.toHost(), + simulatedCard = simulatedCard.toHost(), + simulatedTipAmount = simulatedTipAmount, + ) +} + +fun SimulateReaderUpdateApi.toHost(): SimulateReaderUpdate { + return when (this) { + SimulateReaderUpdateApi.AVAILABLE -> SimulateReaderUpdate.UPDATE_AVAILABLE + SimulateReaderUpdateApi.NONE -> SimulateReaderUpdate.NONE + SimulateReaderUpdateApi.REQUIRED -> SimulateReaderUpdate.REQUIRED + SimulateReaderUpdateApi.RANDOM -> SimulateReaderUpdate.RANDOM + } +} + +fun SimulatedCardApi.toHost(): SimulatedCard { + if (type != null) return SimulatedCard(type.toHost()) + return SimulatedCard(testCardNumber!!) +} + +fun SimulatedCardTypeApi.toHost(): SimulatedCardType { + return when (this) { + SimulatedCardTypeApi.VISA -> SimulatedCardType.VISA + SimulatedCardTypeApi.VISA_DEBIT -> SimulatedCardType.VISA_DEBIT + SimulatedCardTypeApi.VISA_US_COMMON_DEBIT -> SimulatedCardType.VISA_US_COMMON_DEBIT + SimulatedCardTypeApi.MASTERCARD -> SimulatedCardType.MASTERCARD + SimulatedCardTypeApi.MASTER_DEBIT -> SimulatedCardType.MASTERCARD_DEBIT + SimulatedCardTypeApi.MASTERCARD_PREPAID -> SimulatedCardType.MASTERCARD_PREPAID + SimulatedCardTypeApi.AMEX -> SimulatedCardType.AMEX + SimulatedCardTypeApi.AMEX2 -> SimulatedCardType.AMEX_2 + SimulatedCardTypeApi.DISCOVER -> SimulatedCardType.DISCOVER + SimulatedCardTypeApi.DISCOVER2 -> SimulatedCardType.DISCOVER_2 + SimulatedCardTypeApi.DINERS -> SimulatedCardType.DINERS + SimulatedCardTypeApi.DINERS14_DIGIT -> SimulatedCardType.DINERS_14_DIGITS + SimulatedCardTypeApi.JBC -> SimulatedCardType.JCB + SimulatedCardTypeApi.UNION_PAY -> SimulatedCardType.UNION_PAY + SimulatedCardTypeApi.INTERAC -> SimulatedCardType.INTERAC + SimulatedCardTypeApi.EFTPOS_AU_DEBIT -> SimulatedCardType.EFTPOS_AU_DEBIT + SimulatedCardTypeApi.EFTPOS_AU_VISA_DEBIT -> SimulatedCardType.EFTPOS_AU_VISA_DEBIT + SimulatedCardTypeApi.EFTPOS_AU_DEBIT_MASTERCARD -> SimulatedCardType.EFTPOS_AU_DEBIT_MASTERCARD + SimulatedCardTypeApi.CHARGE_DECLINED -> SimulatedCardType.CHARGE_DECLINED + SimulatedCardTypeApi.CHARGE_DECLINED_INSUFFICIENT_FUNDS -> SimulatedCardType.CHARGE_DECLINED_INSUFFICIENT_FUNDS + SimulatedCardTypeApi.CHARGE_DECLINED_LOST_CARD -> SimulatedCardType.CHARGE_DECLINED_LOST_CARD + SimulatedCardTypeApi.CHARGE_DECLINED_STOLEN_CARD -> SimulatedCardType.CHARGE_DECLINED_STOLEN_CARD + SimulatedCardTypeApi.CHARGE_DECLINED_EXPIRED_CARD -> SimulatedCardType.CHARGE_DECLINED_EXPIRED_CARD + SimulatedCardTypeApi.CHARGE_DECLINED_PROCESSING_ERROR -> SimulatedCardType.CHARGE_DECLINED_PROCESSING_ERROR + SimulatedCardTypeApi.ONLINE_PIN_CVM -> SimulatedCardType.ONLINE_PIN_CVM + SimulatedCardTypeApi.ONLINE_PIN_SCA_RETRY -> SimulatedCardType.ONLINE_PIN_SCA_RETRY + SimulatedCardTypeApi.OFFLINE_PIN_CVM -> SimulatedCardType.OFFLINE_PIN_CVM + SimulatedCardTypeApi.OFFLINE_PIN_SCA_RETRY -> SimulatedCardType.OFFLINE_PIN_SCA_RETRY + } +} diff --git a/stripe_terminal/example/lib/main.dart b/stripe_terminal/example/lib/main.dart index 0ae2fff..2150dbc 100644 --- a/stripe_terminal/example/lib/main.dart +++ b/stripe_terminal/example/lib/main.dart @@ -311,10 +311,12 @@ class _HomeScreenState extends State { } void _showSnackBar(String message) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - behavior: SnackBarBehavior.floating, - content: Text(message), - )); + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar(SnackBar( + behavior: SnackBarBehavior.floating, + content: Text(message), + )); } @override diff --git a/stripe_terminal/ios/Classes/Api/TerminalApi.swift b/stripe_terminal/ios/Classes/Api/TerminalApi.swift index 204a6cf..06ce076 100644 --- a/stripe_terminal/ios/Classes/Api/TerminalApi.swift +++ b/stripe_terminal/ios/Classes/Api/TerminalApi.swift @@ -146,6 +146,10 @@ protocol TerminalPlatformApi { func onDisconnectReader() async throws -> Void + func onSetSimulatorConfiguration( + _ configuration: SimulatorConfigurationApi + ) throws -> Void + func onGetPaymentStatus() throws -> PaymentStatusApi func onCreatePaymentIntent( @@ -351,6 +355,9 @@ func setTerminalPlatformApiHandler( try await hostApi.onDisconnectReader() return nil } + case "setSimulatorConfiguration": + let res = try hostApi.onSetSimulatorConfiguration(SimulatorConfigurationApi.deserialize(args[0] as! [Any?])) + result(nil) case "getPaymentStatus": let res = try hostApi.onGetPaymentStatus() result(res.rawValue) @@ -663,30 +670,30 @@ enum CardPresentCaptureMethodApi: Int { struct CardPresentDetailsApi { let brand: CardBrandApi? + let cardholderName: String? let country: String? + let emvAuthData: String? let expMonth: Int let expYear: Int let funding: CardFundingTypeApi? - let last4: String? - let cardholderName: String? - let emvAuthData: String? let generatedCard: String? let incrementalAuthorizationStatus: IncrementalAuthorizationStatusApi? + let last4: String? let networks: CardNetworksApi? let receipt: ReceiptDetailsApi? func serialize() -> [Any?] { return [ brand?.rawValue, + cardholderName, country, + emvAuthData, expMonth, expYear, funding?.rawValue, - last4, - cardholderName, - emvAuthData, generatedCard, incrementalAuthorizationStatus?.rawValue, + last4, networks?.serialize(), receipt?.serialize(), ] @@ -718,62 +725,62 @@ enum CardPresentRoutingApi: Int { struct CartApi { let currency: String + let lineItems: [CartLineItemApi] let tax: Int let total: Int - let lineItems: [CartLineItemApi] static func deserialize( _ serialized: [Any?] ) -> CartApi { return CartApi( currency: serialized[0] as! String, - tax: serialized[1] as! Int, - total: serialized[2] as! Int, - lineItems: (serialized[3] as! [Any?]).map { CartLineItemApi.deserialize($0 as! [Any?]) } + lineItems: (serialized[1] as! [Any?]).map { CartLineItemApi.deserialize($0 as! [Any?]) }, + tax: serialized[2] as! Int, + total: serialized[3] as! Int ) } } struct CartLineItemApi { + let amount: Int let description: String let quantity: Int - let amount: Int static func deserialize( _ serialized: [Any?] ) -> CartLineItemApi { return CartLineItemApi( - description: serialized[0] as! String, - quantity: serialized[1] as! Int, - amount: serialized[2] as! Int + amount: serialized[0] as! Int, + description: serialized[1] as! String, + quantity: serialized[2] as! Int ) } } struct ChargeApi { let amount: Int + let authorizationCode: String? + let calculatedStatementDescriptor: String? let currency: String - let status: ChargeStatusApi - let paymentMethodDetails: PaymentMethodDetailsApi? - let description: String + let description: String? let id: String let metadata: [String: String] + let paymentMethodDetails: PaymentMethodDetailsApi? let statementDescriptorSuffix: String? - let calculatedStatementDescriptor: String? - let authorizationCode: String? + let status: ChargeStatusApi func serialize() -> [Any?] { return [ amount, + authorizationCode, + calculatedStatementDescriptor, currency, - status.rawValue, - paymentMethodDetails?.serialize(), description, id, metadata != nil ? Dictionary(uniqueKeysWithValues: metadata.map { k, v in (k, v) }) : nil, + paymentMethodDetails?.serialize(), statementDescriptorSuffix, - calculatedStatementDescriptor, - authorizationCode, + status.rawValue, ] } } @@ -940,68 +947,68 @@ enum LocationStatusApi: Int { } struct PaymentIntentApi { - let id: String - let created: Date - let status: PaymentIntentStatusApi let amount: Double - let captureMethod: CaptureMethodApi - let currency: String - let metadata: [String: String] - let charges: [ChargeApi] - let paymentMethod: PaymentMethodApi? - let paymentMethodId: String? - let amountDetails: AmountDetailsApi? - let amountTip: Double? - let statementDescriptor: String? - let statementDescriptorSuffix: String? let amountCapturable: Double? + let amountDetails: AmountDetailsApi? let amountReceived: Double? - let applicationId: String? + let amountTip: Double? let applicationFeeAmount: Double? - let cancellationReason: String? + let applicationId: String? let canceledAt: Date? + let cancellationReason: String? + let captureMethod: CaptureMethodApi + let charges: [ChargeApi] let clientSecret: String? let confirmationMethod: ConfirmationMethodApi? + let created: Date + let currency: String let customerId: String? let description: String? + let id: String let invoiceId: String? + let metadata: [String: String] let onBehalfOf: String? - let reviewId: String? + let paymentMethod: PaymentMethodApi? + let paymentMethodId: String? let receiptEmail: String? + let reviewId: String? let setupFutureUsage: PaymentIntentUsageApi? + let statementDescriptor: String? + let statementDescriptorSuffix: String? + let status: PaymentIntentStatusApi let transferGroup: String? func serialize() -> [Any?] { return [ - id, - Int(created.timeIntervalSince1970 * 1000), - status.rawValue, amount, - captureMethod.rawValue, - currency, - metadata != nil ? Dictionary(uniqueKeysWithValues: metadata.map { k, v in (k, v) }) : nil, - charges.map { $0.serialize() }, - paymentMethod?.serialize(), - paymentMethodId, - amountDetails?.serialize(), - amountTip, - statementDescriptor, - statementDescriptorSuffix, amountCapturable, + amountDetails?.serialize(), amountReceived, - applicationId, + amountTip, applicationFeeAmount, - cancellationReason, + applicationId, canceledAt != nil ? Int(canceledAt!.timeIntervalSince1970 * 1000) : nil, + cancellationReason, + captureMethod.rawValue, + charges.map { $0.serialize() }, clientSecret, confirmationMethod?.rawValue, + Int(created.timeIntervalSince1970 * 1000), + currency, customerId, description, + id, invoiceId, + metadata != nil ? Dictionary(uniqueKeysWithValues: metadata.map { k, v in (k, v) }) : nil, onBehalfOf, - reviewId, + paymentMethod?.serialize(), + paymentMethodId, receiptEmail, + reviewId, setupFutureUsage?.rawValue, + statementDescriptor, + statementDescriptorSuffix, + status.rawValue, transferGroup, ] } @@ -1009,42 +1016,42 @@ struct PaymentIntentApi { struct PaymentIntentParametersApi { let amount: Int - let currency: String + let applicationFeeAmount: Int? let captureMethod: CaptureMethodApi - let paymentMethodTypes: [PaymentMethodTypeApi] - let metadata: [String: String] + let currency: String + let customerId: String? let description: String? + let metadata: [String: String] + let onBehalfOf: String? + let paymentMethodOptionsParameters: PaymentMethodOptionsParametersApi? + let paymentMethodTypes: [PaymentMethodTypeApi] + let receiptEmail: String? + let setupFutureUsage: PaymentIntentUsageApi? let statementDescriptor: String? let statementDescriptorSuffix: String? - let receiptEmail: String? - let customerId: String? - let applicationFeeAmount: Int? let transferDataDestination: String? let transferGroup: String? - let onBehalfOf: String? - let setupFutureUsage: PaymentIntentUsageApi? - let paymentMethodOptionsParameters: PaymentMethodOptionsParametersApi? static func deserialize( _ serialized: [Any?] ) -> PaymentIntentParametersApi { return PaymentIntentParametersApi( amount: serialized[0] as! Int, - currency: serialized[1] as! String, + applicationFeeAmount: serialized[1] as? Int, captureMethod: CaptureMethodApi(rawValue: serialized[2] as! Int)!, - paymentMethodTypes: (serialized[3] as! [Any?]).map { PaymentMethodTypeApi(rawValue: $0 as! Int)! }, - metadata: Dictionary(uniqueKeysWithValues: (serialized[4] as! [AnyHashable?: Any?]).map { k, v in (k as! String, v as! String) }), + currency: serialized[3] as! String, + customerId: serialized[4] as? String, description: serialized[5] as? String, - statementDescriptor: serialized[6] as? String, - statementDescriptorSuffix: serialized[7] as? String, - receiptEmail: serialized[8] as? String, - customerId: serialized[9] as? String, - applicationFeeAmount: serialized[10] as? Int, - transferDataDestination: serialized[11] as? String, - transferGroup: serialized[12] as? String, - onBehalfOf: serialized[13] as? String, - setupFutureUsage: !(serialized[14] is NSNull) ? PaymentIntentUsageApi(rawValue: serialized[14] as! Int)! : nil, - paymentMethodOptionsParameters: !(serialized[15] is NSNull) ? PaymentMethodOptionsParametersApi.deserialize(serialized[15] as! [Any?]) : nil + metadata: Dictionary(uniqueKeysWithValues: (serialized[6] as! [AnyHashable?: Any?]).map { k, v in (k as! String, v as! String) }), + onBehalfOf: serialized[7] as? String, + paymentMethodOptionsParameters: !(serialized[8] is NSNull) ? PaymentMethodOptionsParametersApi.deserialize(serialized[8] as! [Any?]) : nil, + paymentMethodTypes: (serialized[9] as! [Any?]).map { PaymentMethodTypeApi(rawValue: $0 as! Int)! }, + receiptEmail: serialized[10] as? String, + setupFutureUsage: !(serialized[11] is NSNull) ? PaymentIntentUsageApi(rawValue: serialized[11] as! Int)! : nil, + statementDescriptor: serialized[12] as? String, + statementDescriptorSuffix: serialized[13] as? String, + transferDataDestination: serialized[14] as? String, + transferGroup: serialized[15] as? String ) } } @@ -1065,20 +1072,20 @@ enum PaymentIntentUsageApi: Int { } struct PaymentMethodApi { - let id: String let card: CardDetailsApi? let cardPresent: CardPresentDetailsApi? - let interacPresent: CardPresentDetailsApi? let customerId: String? + let id: String + let interacPresent: CardPresentDetailsApi? let metadata: [String: String] func serialize() -> [Any?] { return [ - id, card?.serialize(), cardPresent?.serialize(), - interacPresent?.serialize(), customerId, + id, + interacPresent?.serialize(), metadata != nil ? Dictionary(uniqueKeysWithValues: metadata.map { k, v in (k, v) }) : nil, ] } @@ -1122,27 +1129,27 @@ enum PaymentStatusApi: Int { } struct ReaderApi { - let locationStatus: LocationStatusApi? - let deviceType: DeviceTypeApi? - let simulated: Bool - let locationId: String? - let location: LocationApi? - let serialNumber: String let availableUpdate: Bool let batteryLevel: Double + let deviceType: DeviceTypeApi? let label: String? + let location: LocationApi? + let locationId: String? + let locationStatus: LocationStatusApi? + let serialNumber: String + let simulated: Bool func serialize() -> [Any?] { return [ - locationStatus?.rawValue, - deviceType?.rawValue, - simulated, - locationId, - location?.serialize(), - serialNumber, availableUpdate, batteryLevel, + deviceType?.rawValue, label, + location?.serialize(), + locationId, + locationStatus?.rawValue, + serialNumber, + simulated, ] } } @@ -1196,52 +1203,52 @@ struct ReaderSoftwareUpdateApi { struct ReceiptDetailsApi { let accountType: String? - let applicationPreferredName: String + let applicationCryptogram: String? + let applicationPreferredName: String? let authorizationCode: String? let authorizationResponseCode: String - let applicationCryptogram: String - let dedicatedFileName: String - let transactionStatusInformation: String - let terminalVerificationResults: String + let dedicatedFileName: String? + let terminalVerificationResults: String? + let transactionStatusInformation: String? func serialize() -> [Any?] { return [ accountType, + applicationCryptogram, applicationPreferredName, authorizationCode, authorizationResponseCode, - applicationCryptogram, dedicatedFileName, - transactionStatusInformation, terminalVerificationResults, + transactionStatusInformation, ] } } struct RefundApi { - let id: String let amount: Int let chargeId: String let created: Date let currency: String + let failureReason: String? + let id: String let metadata: [String: String] + let paymentMethodDetails: PaymentMethodDetailsApi? let reason: String? let status: RefundStatusApi? - let paymentMethodDetails: PaymentMethodDetailsApi? - let failureReason: String? func serialize() -> [Any?] { return [ - id, amount, chargeId, Int(created.timeIntervalSince1970 * 1000), currency, + failureReason, + id, metadata != nil ? Dictionary(uniqueKeysWithValues: metadata.map { k, v in (k, v) }) : nil, + paymentMethodDetails?.serialize(), reason, status?.rawValue, - paymentMethodDetails?.serialize(), - failureReason, ] } } @@ -1253,25 +1260,25 @@ enum RefundStatusApi: Int { } struct SetupAttemptApi { - let id: String let applicationId: String? let created: Date let customerId: String? + let id: String let onBehalfOf: String? - let paymentMethodId: String? let paymentMethodDetails: SetupAttemptPaymentMethodDetailsApi? + let paymentMethodId: String? let setupIntentId: String let status: SetupAttemptStatusApi func serialize() -> [Any?] { return [ - id, applicationId, Int(created.timeIntervalSince1970 * 1000), customerId, + id, onBehalfOf, - paymentMethodId, paymentMethodDetails?.serialize(), + paymentMethodId, setupIntentId, status.rawValue, ] @@ -1312,23 +1319,23 @@ enum SetupAttemptStatusApi: Int { } struct SetupIntentApi { - let id: String let created: Date let customerId: String? + let id: String + let latestAttempt: SetupAttemptApi? let metadata: [String: String] - let usage: SetupIntentUsageApi let status: SetupIntentStatusApi - let latestAttempt: SetupAttemptApi? + let usage: SetupIntentUsageApi func serialize() -> [Any?] { return [ - id, Int(created.timeIntervalSince1970 * 1000), customerId, + id, + latestAttempt?.serialize(), metadata != nil ? Dictionary(uniqueKeysWithValues: metadata.map { k, v in (k, v) }) : nil, - usage.rawValue, status.rawValue, - latestAttempt?.serialize(), + usage.rawValue, ] } } @@ -1347,20 +1354,88 @@ enum SetupIntentUsageApi: Int { case offSession } +enum SimulateReaderUpdateApi: Int { + case available + case none + case required + case random +} + +struct SimulatedCardApi { + let testCardNumber: String? + let type: SimulatedCardTypeApi? + + static func deserialize( + _ serialized: [Any?] + ) -> SimulatedCardApi { + return SimulatedCardApi( + testCardNumber: serialized[0] as? String, + type: !(serialized[1] is NSNull) ? SimulatedCardTypeApi(rawValue: serialized[1] as! Int)! : nil + ) + } +} + +enum SimulatedCardTypeApi: Int { + case visa + case visaDebit + case visaUsCommonDebit + case mastercard + case masterDebit + case mastercardPrepaid + case amex + case amex2 + case discover + case discover2 + case diners + case diners14Digit + case jbc + case unionPay + case interac + case eftposAuDebit + case eftposAuVisaDebit + case eftposAuDebitMastercard + case chargeDeclined + case chargeDeclinedInsufficientFunds + case chargeDeclinedLostCard + case chargeDeclinedStolenCard + case chargeDeclinedExpiredCard + case chargeDeclinedProcessingError + case onlinePinCvm + case onlinePinScaRetry + case offlinePinCvm + case offlinePinScaRetry +} + +struct SimulatorConfigurationApi { + let simulatedCard: SimulatedCardApi + let simulatedTipAmount: Int? + let update: SimulateReaderUpdateApi + + static func deserialize( + _ serialized: [Any?] + ) -> SimulatorConfigurationApi { + return SimulatorConfigurationApi( + simulatedCard: SimulatedCardApi.deserialize(serialized[0] as! [Any?]), + simulatedTipAmount: serialized[1] as? Int, + update: SimulateReaderUpdateApi(rawValue: serialized[2] as! Int)! + ) + } +} + struct TerminalExceptionApi { + let apiError: Any? let code: TerminalExceptionCodeApi let message: String - let stackTrace: String? let paymentIntent: PaymentIntentApi? - let apiError: Any? + let stackTrace: String? func serialize() -> [Any?] { return [ + apiError, code.rawValue, message, - stackTrace, paymentIntent?.serialize(), - apiError, + stackTrace, ] } } diff --git a/stripe_terminal/ios/Classes/Mappings/CardMappings.swift b/stripe_terminal/ios/Classes/Mappings/CardMappings.swift index a7e4532..0557f55 100644 --- a/stripe_terminal/ios/Classes/Mappings/CardMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/CardMappings.swift @@ -65,15 +65,15 @@ extension CardPresentDetails { func toApi() -> CardPresentDetailsApi { return CardPresentDetailsApi( brand: brand.toApi(), + cardholderName: cardholderName, country: country, + emvAuthData: emvAuthData, expMonth: expMonth, expYear: expYear, funding: funding.toApi(), - last4: last4, - cardholderName: cardholderName, - emvAuthData: emvAuthData, generatedCard: generatedCard, incrementalAuthorizationStatus: incrementalAuthorizationStatus.toApi(), + last4: last4, networks: networks?.toApi(), receipt: receipt?.toApi() ) @@ -108,13 +108,13 @@ extension ReceiptDetails { func toApi() -> ReceiptDetailsApi { return ReceiptDetailsApi( accountType: accountType, + applicationCryptogram: applicationCryptogram, applicationPreferredName: applicationPreferredName, authorizationCode: authorizationCode, authorizationResponseCode: authorizationResponseCode, - applicationCryptogram: applicationCryptogram, dedicatedFileName: dedicatedFileName, - transactionStatusInformation: transactionStatusInformation, - terminalVerificationResults: terminalVerificationResults + terminalVerificationResults: terminalVerificationResults, + transactionStatusInformation: transactionStatusInformation ) } } diff --git a/stripe_terminal/ios/Classes/Mappings/ChargeMappings.swift b/stripe_terminal/ios/Classes/Mappings/ChargeMappings.swift index da5d45e..2e3d480 100644 --- a/stripe_terminal/ios/Classes/Mappings/ChargeMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/ChargeMappings.swift @@ -20,15 +20,15 @@ extension Charge { func toApi() -> ChargeApi { return ChargeApi( amount: amount.intValue, + authorizationCode: authorizationCode, + calculatedStatementDescriptor: calculatedStatementDescriptor, currency: currency, - status: status.toApi(), - paymentMethodDetails: paymentMethodDetails?.toApi(), description: description, id: stripeId, metadata: metadata, + paymentMethodDetails: paymentMethodDetails?.toApi(), statementDescriptorSuffix: statementDescriptorSuffix, - calculatedStatementDescriptor: calculatedStatementDescriptor, - authorizationCode: authorizationCode + status: status.toApi() ) } } diff --git a/stripe_terminal/ios/Classes/Mappings/ExceptionMappings.swift b/stripe_terminal/ios/Classes/Mappings/ExceptionMappings.swift index 78248a6..45c8368 100644 --- a/stripe_terminal/ios/Classes/Mappings/ExceptionMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/ExceptionMappings.swift @@ -19,11 +19,11 @@ extension NSError { func toApi(apiError: Error? = nil, paymentIntent: PaymentIntent? = nil) -> TerminalExceptionApi { let code = self.toApiCode(); return TerminalExceptionApi( + apiError: apiError?.localizedDescription, code: code ?? TerminalExceptionCodeApi.unknown, message: localizedDescription, - stackTrace: nil, paymentIntent: paymentIntent?.toApi(), - apiError: apiError?.localizedDescription + stackTrace: nil ) } diff --git a/stripe_terminal/ios/Classes/Mappings/PaymentIntentMappings.swift b/stripe_terminal/ios/Classes/Mappings/PaymentIntentMappings.swift index 7480186..0d296d5 100644 --- a/stripe_terminal/ios/Classes/Mappings/PaymentIntentMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/PaymentIntentMappings.swift @@ -4,36 +4,35 @@ import StripeTerminal extension PaymentIntent { func toApi() -> PaymentIntentApi { return PaymentIntentApi( - id: stripeId!, - created: created, - status: status.toApi(), amount: Double(amount), - captureMethod: captureMethod.toApi(), - currency: currency, - metadata: metadata ?? [:], - charges: charges.map { $0.toApi() }, - paymentMethod: paymentMethod?.toApi(), - paymentMethodId: paymentMethodId, - amountDetails: amountDetails?.toApi(), - amountTip: amountTip != nil ? Double(truncating: amountTip!) : nil, - statementDescriptor: statementDescriptor, - statementDescriptorSuffix: statementDescriptorSuffix, - // Only Android amountCapturable: nil, + amountDetails: amountDetails?.toApi(), amountReceived: nil, - applicationId: nil, + amountTip: amountTip != nil ? Double(truncating: amountTip!) : nil, applicationFeeAmount: nil, - cancellationReason: nil, + applicationId: nil, canceledAt: nil, + cancellationReason: nil, + captureMethod: captureMethod.toApi(), + charges: charges.map { $0.toApi() }, clientSecret: nil, confirmationMethod: nil, + created: created, + currency: currency, customerId: nil, description: description, + id: stripeId!, invoiceId: nil, + metadata: metadata ?? [:], onBehalfOf: nil, - reviewId: nil, + paymentMethod: paymentMethod?.toApi(), + paymentMethodId: paymentMethodId, receiptEmail: nil, + reviewId: nil, setupFutureUsage: nil, + statementDescriptor: statementDescriptor, + statementDescriptorSuffix: statementDescriptorSuffix, + status: status.toApi(), transferGroup: nil ) } diff --git a/stripe_terminal/ios/Classes/Mappings/PaymentMethodMappings.swift b/stripe_terminal/ios/Classes/Mappings/PaymentMethodMappings.swift index c51d228..e04c32c 100644 --- a/stripe_terminal/ios/Classes/Mappings/PaymentMethodMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/PaymentMethodMappings.swift @@ -4,11 +4,11 @@ import StripeTerminal extension PaymentMethod { func toApi() -> PaymentMethodApi { return PaymentMethodApi( - id: stripeId, card: card?.toApi(), cardPresent: cardPresent?.toApi(), - interacPresent: interacPresent?.toApi(), customerId: customer, + id: stripeId, + interacPresent: interacPresent?.toApi(), metadata: metadata ) } diff --git a/stripe_terminal/ios/Classes/Mappings/ReaderMappings.swift b/stripe_terminal/ios/Classes/Mappings/ReaderMappings.swift index df586dd..b13adec 100644 --- a/stripe_terminal/ios/Classes/Mappings/ReaderMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/ReaderMappings.swift @@ -5,15 +5,15 @@ import StripeTerminal extension Reader { func toApi() -> ReaderApi { return ReaderApi( - locationStatus: locationStatus.toApi(), + availableUpdate: availableUpdate != nil, + batteryLevel: batteryLevel?.doubleValue ?? -1.0, deviceType: deviceType.toApi(), - simulated: simulated, - locationId: locationId, + label: label, location: location?.toApi(), + locationId: locationId, + locationStatus: locationStatus.toApi(), serialNumber: serialNumber, - availableUpdate: availableUpdate != nil, - batteryLevel: batteryLevel?.doubleValue ?? -1.0, - label: label + simulated: simulated ) } } diff --git a/stripe_terminal/ios/Classes/Mappings/RefundMappings.swift b/stripe_terminal/ios/Classes/Mappings/RefundMappings.swift index d446580..d4d3d74 100644 --- a/stripe_terminal/ios/Classes/Mappings/RefundMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/RefundMappings.swift @@ -4,16 +4,16 @@ import StripeTerminal extension Refund { func toApi() -> RefundApi { return RefundApi( - id: stripeId, amount: Int(amount), chargeId: charge, created: created, currency: currency, + failureReason: failureReason, + id: stripeId, metadata: metadata, - reason: reason, - status: status.toApi(), paymentMethodDetails: paymentMethodDetails?.toApi(), - failureReason: failureReason + reason: reason, + status: status.toApi() ) } } diff --git a/stripe_terminal/ios/Classes/Mappings/SetupIntentMappings.swift b/stripe_terminal/ios/Classes/Mappings/SetupIntentMappings.swift index 67af37c..ea6abae 100644 --- a/stripe_terminal/ios/Classes/Mappings/SetupIntentMappings.swift +++ b/stripe_terminal/ios/Classes/Mappings/SetupIntentMappings.swift @@ -4,13 +4,13 @@ import StripeTerminal extension SetupIntent { func toApi() -> SetupIntentApi { return SetupIntentApi( - id : stripeId, created: created, customerId: customer, + id: stripeId, + latestAttempt: latestAttempt?.toApi(), metadata: metadata ?? [:], - usage: usage.toApi(), status: status.toApi(), - latestAttempt: latestAttempt?.toApi() + usage: usage.toApi() ) } } @@ -69,13 +69,13 @@ extension SetupAttempt { fatalError() } return SetupAttemptApi( - id : stripeId, applicationId: application, created: created, customerId: customer, + id : stripeId, onBehalfOf: onBehalfOf, - paymentMethodId: paymentMethod, paymentMethodDetails: paymentMethodDetails?.toApi(), + paymentMethodId: paymentMethod, setupIntentId: setupIntent, status: statusApi ) diff --git a/stripe_terminal/ios/Classes/Mappings/SimulatorConfigurationMappings.swift b/stripe_terminal/ios/Classes/Mappings/SimulatorConfigurationMappings.swift new file mode 100644 index 0000000..bc8110e --- /dev/null +++ b/stripe_terminal/ios/Classes/Mappings/SimulatorConfigurationMappings.swift @@ -0,0 +1,97 @@ +// +// SimulatorConfigurationMappings.swift +// mek_stripe_terminal +// +// Created by Kuama on 25/11/23. +// + +import Foundation +import StripeTerminal + +extension SimulateReaderUpdateApi { + func toHost() -> SimulateReaderUpdate { + switch self { + case SimulateReaderUpdateApi.available: + return SimulateReaderUpdate.available + case SimulateReaderUpdateApi.none: + return SimulateReaderUpdate.none + case SimulateReaderUpdateApi.required: + return SimulateReaderUpdate.required + case SimulateReaderUpdateApi.random: + return SimulateReaderUpdate.random + } + } +} + +extension SimulatedCardApi { + func toHost() -> SimulatedCard { + if let type { + return SimulatedCard(type: type.toHost()) + } + return SimulatedCard(testCardNumber: testCardNumber!) + } + +} + +extension SimulatedCardTypeApi { + func toHost() -> SimulatedCardType { + switch self { + case SimulatedCardTypeApi.visa: + return SimulatedCardType.visa + case SimulatedCardTypeApi.visaDebit: + return SimulatedCardType.visaDebit + case SimulatedCardTypeApi.visaUsCommonDebit: + return SimulatedCardType.visaUsCommonDebit + case SimulatedCardTypeApi.mastercard: + return SimulatedCardType.mastercard + case SimulatedCardTypeApi.masterDebit: + return SimulatedCardType.masterDebit + case SimulatedCardTypeApi.mastercardPrepaid: + return SimulatedCardType.mastercardPrepaid + case SimulatedCardTypeApi.amex: + return SimulatedCardType.amex + case SimulatedCardTypeApi.amex2: + return SimulatedCardType.amex2 + case SimulatedCardTypeApi.discover: + return SimulatedCardType.discover + case SimulatedCardTypeApi.discover2: + return SimulatedCardType.discover2 + case SimulatedCardTypeApi.diners: + return SimulatedCardType.diners + case SimulatedCardTypeApi.diners14Digit: + return SimulatedCardType.diners14Digit + case SimulatedCardTypeApi.jbc: + return SimulatedCardType.jcb + case SimulatedCardTypeApi.unionPay: + return SimulatedCardType.unionPay + case SimulatedCardTypeApi.interac: + return SimulatedCardType.interac + case SimulatedCardTypeApi.eftposAuDebit: + return SimulatedCardType.eftposAuDebit + case SimulatedCardTypeApi.eftposAuVisaDebit: + return SimulatedCardType.eftposAuDebit + case SimulatedCardTypeApi.eftposAuDebitMastercard: + return SimulatedCardType.eftposAuDebitMastercard + case SimulatedCardTypeApi.chargeDeclined: + return SimulatedCardType.chargeDeclined + case SimulatedCardTypeApi.chargeDeclinedInsufficientFunds: + return SimulatedCardType.chargeDeclinedInsufficientFunds + case SimulatedCardTypeApi.chargeDeclinedLostCard: + return SimulatedCardType.chargeDeclinedLostCard + case SimulatedCardTypeApi.chargeDeclinedStolenCard: + return SimulatedCardType.chargeDeclinedStolenCard + case SimulatedCardTypeApi.chargeDeclinedExpiredCard: + return SimulatedCardType.chargeDeclinedExpiredCard + case SimulatedCardTypeApi.chargeDeclinedProcessingError: + return SimulatedCardType.chargeDeclinedProcessingError + case SimulatedCardTypeApi.onlinePinCvm: + return SimulatedCardType.onlinePinCvm + case SimulatedCardTypeApi.onlinePinScaRetry: + return SimulatedCardType.onlinePinScaRetry + case SimulatedCardTypeApi.offlinePinCvm: + return SimulatedCardType.offlinePinCvm + case SimulatedCardTypeApi.offlinePinScaRetry: + return SimulatedCardType.offlinePinScaRetry + } + } +} diff --git a/stripe_terminal/ios/Classes/TerminalPlugin.swift b/stripe_terminal/ios/Classes/TerminalPlugin.swift index b484a2c..31cd6b2 100644 --- a/stripe_terminal/ios/Classes/TerminalPlugin.swift +++ b/stripe_terminal/ios/Classes/TerminalPlugin.swift @@ -189,6 +189,12 @@ public class TerminalPlugin: NSObject, FlutterPlugin, TerminalPlatformApi { } } + func onSetSimulatorConfiguration(_ configuration: SimulatorConfigurationApi) throws { + Terminal.shared.simulatorConfiguration.availableReaderUpdate = configuration.update.toHost() + Terminal.shared.simulatorConfiguration.simulatedCard = configuration.simulatedCard.toHost() + Terminal.shared.simulatorConfiguration.simulatedTipAmount = configuration.simulatedTipAmount?.nsNumberValue + } + // MARK: - Taking payments private var _paymentIntents: [String: PaymentIntent] = [:] diff --git a/stripe_terminal/ios/Classes/Utils.swift b/stripe_terminal/ios/Classes/Utils.swift index 4ffb67e..944c455 100644 --- a/stripe_terminal/ios/Classes/Utils.swift +++ b/stripe_terminal/ios/Classes/Utils.swift @@ -2,10 +2,10 @@ import Foundation func createApiException(_ code: TerminalExceptionCodeApi, _ message: String? = nil) -> TerminalExceptionApi { return TerminalExceptionApi( + apiError: nil, code: code, message: message ?? "", - stackTrace: nil, paymentIntent: nil, - apiError: nil + stackTrace: nil ) } diff --git a/stripe_terminal/lib/mek_stripe_terminal.dart b/stripe_terminal/lib/mek_stripe_terminal.dart index c0a69c7..b392ae2 100644 --- a/stripe_terminal/lib/mek_stripe_terminal.dart +++ b/stripe_terminal/lib/mek_stripe_terminal.dart @@ -15,6 +15,7 @@ export 'src/models/reader.dart'; export 'src/models/reader_software_update.dart'; export 'src/models/refund.dart'; export 'src/models/setup_intent.dart'; +export 'src/models/simultator_configuration.dart'; export 'src/models/tip.dart'; export 'src/reader_delegates.dart'; export 'src/terminal.dart'; diff --git a/stripe_terminal/lib/src/models/card.dart b/stripe_terminal/lib/src/models/card.dart index b246e3c..3b09eae 100644 --- a/stripe_terminal/lib/src/models/card.dart +++ b/stripe_terminal/lib/src/models/card.dart @@ -90,13 +90,13 @@ class CardNetworks with _$CardNetworks { @DataClass() class ReceiptDetails with _$ReceiptDetails { final String? accountType; - final String applicationPreferredName; + final String? applicationPreferredName; final String? authorizationCode; final String authorizationResponseCode; - final String applicationCryptogram; - final String dedicatedFileName; - final String transactionStatusInformation; - final String terminalVerificationResults; + final String? applicationCryptogram; + final String? dedicatedFileName; + final String? transactionStatusInformation; + final String? terminalVerificationResults; const ReceiptDetails({ required this.accountType, diff --git a/stripe_terminal/lib/src/models/charge.dart b/stripe_terminal/lib/src/models/charge.dart index c1b3d9f..cf51bc6 100644 --- a/stripe_terminal/lib/src/models/charge.dart +++ b/stripe_terminal/lib/src/models/charge.dart @@ -34,7 +34,7 @@ class Charge with _$Charge { final PaymentMethodDetails? paymentMethodDetails; /// A string describing the charge, displayed in the Stripe dashboard and in email receipts. - final String description; + final String? description; /// The unique identifier for the charge. final String id; diff --git a/stripe_terminal/lib/src/models/simultator_configuration.dart b/stripe_terminal/lib/src/models/simultator_configuration.dart new file mode 100644 index 0000000..0c38b47 --- /dev/null +++ b/stripe_terminal/lib/src/models/simultator_configuration.dart @@ -0,0 +1,168 @@ +/// Simulator specific configurations you can set to test your integration’s behavior in different +/// scenarios. We recommend changing these properties during testing to ensure your app works as +/// expected for different reader updates and for different presented cards. +/// +/// Do not create new instances of this class. Instead, set the properties via [Terminal.setSimulatorConfiguration]. +class SimulatorConfiguration { + /// Set this to different values of the [SimulateReaderUpdate] enum to test your integration with + /// different reader software update scenarios. + final SimulateReaderUpdate update; + + /// Create a [SimulatedCard] and set it on the shared configuration object to test your integration + /// with different card brands and in error scenarios. + /// + /// Note: Simulated Internet reader refunds do not use the specified simulated card. + /// See: https://stripe.com/docs/terminal/testing#simulated-test-cards + final SimulatedCard simulatedCard; + + /// Set this to simulate a [Terminal] configuration object with this fixed tip amount for all currencies. + final int? simulatedTipAmount; + + const SimulatorConfiguration({ + this.update = SimulateReaderUpdate.available, + this.simulatedCard = const SimulatedCard.fromType(SimulatedCardType.visa), + this.simulatedTipAmount, + }); +} + +/// Enum used to simulate various types of reader updates being available for a simulated bluetooth +/// or local mobile reader. +enum SimulateReaderUpdate { + /// Updates are available + /// + /// When connecting to a Bluetooth reader, an update is available that is marked as needing to be installed within 7 days. + /// When connecting to a Local Mobile reader, a mandatory update will complete during the connection flow. + available, + + /// No updates are available + none, + + /// A required full reader software update exists. + /// + /// Use this to simulate the auto-install of a required update that will be applied during connect. + /// This simulated update will take 1 minute and progress will be provided to the delegate provided + /// to [Terminal.connectBluetoothReader] or [Terminal.connectLocalMobileReader] as appropriate. + required, + + /// Randomly picks a type of update for the reader to help exercise the various states. + random, +} + +/// Simulated Card objects can be used with the shared [SimulatorConfiguration] to simulate different +/// card brand and error cases with a simulated Reader. +/// +/// Simulated Card objects are backed by one of Stripe’s test card numbers, which are hardcoded to +/// provide certain behavior within Stripe’s backend. The Terminal SDK provides an [SimulatedCardType] +/// enum that automatically maps to the card numbers for convenience. +/// +/// +/// See: https://stripe.com/docs/terminal/testing#simulated-test-cards +class SimulatedCard { + final SimulatedCardType? type; + final String? testCardNumber; + + /// Create a Simulated Card object with a given simulated card type. + const SimulatedCard.fromType(SimulatedCardType this.type) : testCardNumber = null; + + /// Create a Simulated Card object with a raw card number. + /// + /// This initializer is made available in case Stripe creates a new test card number without + /// creating a corresponding [SimulatedCardType]. The card number entered here must be in the list + /// of test card numbers. + /// + /// See: https://stripe.com/docs/terminal/testing#simulated-test-cards + const SimulatedCard.fromTestCardNumber(String this.testCardNumber) : type = null; +} + +/// Enum used to simulate various types of cards and error cases. +/// +/// See: https://stripe.com/docs/terminal/testing#simulated-test-cards +enum SimulatedCardType { + /// Visa + visa, + + /// Visa (debit) + visaDebit, + + /// Visa debit supporting both international and US Common Debit applications + visaUsCommonDebit, + + /// Mastercard + mastercard, + + /// Mastercard (debit) + masterDebit, + + /// Mastercard (prepaid) + mastercardPrepaid, + + /// American Express + amex, + + /// American Express + amex2, + + /// Discover + discover, + + /// Discover + discover2, + + /// Diners Club + diners, + + /// Diners Club (14 digit card) + diners14Digit, + + /// JCB + jbc, + + /// UnionPay + unionPay, + + /// Interac + interac, + + /// Eftpos Australia + eftposAuDebit, + + /// Eftpos Australia/Visa + eftposAuVisaDebit, + + /// Eftpos Australia/Mastercard + eftposAuDebitMastercard, + + /// Charge is declined with a card_declined code. + chargeDeclined, + + /// Charge is declined with a card_declined code. The decline_code attribute is insufficient_funds. + chargeDeclinedInsufficientFunds, + + /// Charge is declined with a card_declined code. The decline_code attribute is lost_card. + chargeDeclinedLostCard, + + /// Charge is declined with a card_declined code. The decline_code attribute is stolen_card. + chargeDeclinedStolenCard, + + /// Charge is declined with an expired_card code. + chargeDeclinedExpiredCard, + + /// Charge is declined with a processing_error code. + chargeDeclinedProcessingError, + + /// Payment attaches Online Pin to the transaction. cardholder_verification_method will be set to + /// online_pin in the resulting paymentIntent WisePad3 only + onlinePinCvm, + + /// This flow simulates an Online Pin scenario with SCA compliance. Payment is retried and user is + /// prompted to input their pin. Next an online pin being entered is simulated. + onlinePinScaRetry, + + /// Payment attaches Offline Pin to the transaction. cardholder_verification_method will be set to + /// offline_pin in the resulting paymentIntent WisePad3 only + offlinePinCvm, + + /// This flow simulates an Offline Pin scenario with SCA compliance. Payment is retried and user is + /// prompted to insert their card. Next a contact retry and an offline pin being entered are simulated. + offlinePinScaRetry, +} diff --git a/stripe_terminal/lib/src/platform/terminal_platform.api.dart b/stripe_terminal/lib/src/platform/terminal_platform.api.dart index 56c2b20..14905c2 100644 --- a/stripe_terminal/lib/src/platform/terminal_platform.api.dart +++ b/stripe_terminal/lib/src/platform/terminal_platform.api.dart @@ -4,12 +4,13 @@ part of 'terminal_platform.dart'; -class _$TerminalPlatform { +class _$TerminalPlatform implements TerminalPlatform { static const _$channel = MethodChannel('mek_stripe_terminal#TerminalPlatform'); static const _$discoverReaders = EventChannel('mek_stripe_terminal#TerminalPlatform#discoverReaders'); + @override Stream> discoverReaders(DiscoveryConfiguration configuration) { return _$discoverReaders .receiveBroadcastStream([_$serializeDiscoveryConfiguration(configuration)]) @@ -20,6 +21,7 @@ class _$TerminalPlatform { }); } + @override Future init({required bool shouldPrintLogs}) async { try { await _$channel.invokeMethod('init', [shouldPrintLogs]); @@ -29,6 +31,7 @@ class _$TerminalPlatform { } } + @override Future clearCachedCredentials() async { try { await _$channel.invokeMethod('clearCachedCredentials', []); @@ -38,6 +41,7 @@ class _$TerminalPlatform { } } + @override Future getConnectionStatus() async { try { final result = await _$channel.invokeMethod('getConnectionStatus', []); @@ -48,6 +52,7 @@ class _$TerminalPlatform { } } + @override Future supportsReadersOfType({ required DeviceType? deviceType, required DiscoveryConfiguration discoveryConfiguration, @@ -62,6 +67,7 @@ class _$TerminalPlatform { } } + @override Future connectBluetoothReader( String serialNumber, { required String locationId, @@ -77,6 +83,7 @@ class _$TerminalPlatform { } } + @override Future connectHandoffReader(String serialNumber) async { try { final result = await _$channel.invokeMethod('connectHandoffReader', [serialNumber]); @@ -87,6 +94,7 @@ class _$TerminalPlatform { } } + @override Future connectInternetReader( String serialNumber, { required bool failIfInUse, @@ -101,6 +109,7 @@ class _$TerminalPlatform { } } + @override Future connectMobileReader( String serialNumber, { required String locationId, @@ -115,6 +124,7 @@ class _$TerminalPlatform { } } + @override Future connectUsbReader( String serialNumber, { required String locationId, @@ -130,6 +140,7 @@ class _$TerminalPlatform { } } + @override Future getConnectedReader() async { try { final result = await _$channel.invokeMethod('getConnectedReader', []); @@ -140,6 +151,7 @@ class _$TerminalPlatform { } } + @override Future cancelReaderReconnection() async { try { await _$channel.invokeMethod('cancelReaderReconnection', []); @@ -149,6 +161,7 @@ class _$TerminalPlatform { } } + @override Future> listLocations({ required String? endingBefore, required int? limit, @@ -164,6 +177,7 @@ class _$TerminalPlatform { } } + @override Future installAvailableUpdate() async { try { await _$channel.invokeMethod('installAvailableUpdate', []); @@ -173,6 +187,7 @@ class _$TerminalPlatform { } } + @override Future cancelReaderUpdate() async { try { await _$channel.invokeMethod('cancelReaderUpdate', []); @@ -182,6 +197,7 @@ class _$TerminalPlatform { } } + @override Future disconnectReader() async { try { await _$channel.invokeMethod('disconnectReader', []); @@ -191,6 +207,18 @@ class _$TerminalPlatform { } } + @override + Future setSimulatorConfiguration(SimulatorConfiguration configuration) async { + try { + await _$channel.invokeMethod( + 'setSimulatorConfiguration', [_$serializeSimulatorConfiguration(configuration)]); + } on PlatformException catch (exception) { + TerminalPlatform._throwIfIsHostException(exception); + rethrow; + } + } + + @override Future getPaymentStatus() async { try { final result = await _$channel.invokeMethod('getPaymentStatus', []); @@ -201,6 +229,7 @@ class _$TerminalPlatform { } } + @override Future createPaymentIntent(PaymentIntentParameters parameters) async { try { final result = await _$channel @@ -212,6 +241,7 @@ class _$TerminalPlatform { } } + @override Future retrievePaymentIntent(String clientSecret) async { try { final result = await _$channel.invokeMethod('retrievePaymentIntent', [clientSecret]); @@ -222,6 +252,7 @@ class _$TerminalPlatform { } } + @override Future startCollectPaymentMethod({ required int operationId, required String paymentIntentId, @@ -246,6 +277,7 @@ class _$TerminalPlatform { } } + @override Future stopCollectPaymentMethod(int operationId) async { try { await _$channel.invokeMethod('stopCollectPaymentMethod', [operationId]); @@ -255,6 +287,7 @@ class _$TerminalPlatform { } } + @override Future confirmPaymentIntent(String paymentIntentId) async { try { final result = await _$channel.invokeMethod('confirmPaymentIntent', [paymentIntentId]); @@ -265,6 +298,7 @@ class _$TerminalPlatform { } } + @override Future cancelPaymentIntent(String paymentIntentId) async { try { final result = await _$channel.invokeMethod('cancelPaymentIntent', [paymentIntentId]); @@ -275,6 +309,7 @@ class _$TerminalPlatform { } } + @override Future createSetupIntent({ required String? customerId, required Map? metadata, @@ -297,6 +332,7 @@ class _$TerminalPlatform { } } + @override Future retrieveSetupIntent(String clientSecret) async { try { final result = await _$channel.invokeMethod('retrieveSetupIntent', [clientSecret]); @@ -307,6 +343,7 @@ class _$TerminalPlatform { } } + @override Future startCollectSetupIntentPaymentMethod({ required int operationId, required String setupIntentId, @@ -323,6 +360,7 @@ class _$TerminalPlatform { } } + @override Future stopCollectSetupIntentPaymentMethod(int operationId) async { try { await _$channel.invokeMethod('stopCollectSetupIntentPaymentMethod', [operationId]); @@ -332,6 +370,7 @@ class _$TerminalPlatform { } } + @override Future confirmSetupIntent(String setupIntentId) async { try { final result = await _$channel.invokeMethod('confirmSetupIntent', [setupIntentId]); @@ -342,6 +381,7 @@ class _$TerminalPlatform { } } + @override Future cancelSetupIntent(String setupIntentId) async { try { final result = await _$channel.invokeMethod('cancelSetupIntent', [setupIntentId]); @@ -352,6 +392,7 @@ class _$TerminalPlatform { } } + @override Future startCollectRefundPaymentMethod({ required int operationId, required String chargeId, @@ -379,6 +420,7 @@ class _$TerminalPlatform { } } + @override Future stopCollectRefundPaymentMethod(int operationId) async { try { await _$channel.invokeMethod('stopCollectRefundPaymentMethod', [operationId]); @@ -388,6 +430,7 @@ class _$TerminalPlatform { } } + @override Future confirmRefund() async { try { final result = await _$channel.invokeMethod('confirmRefund', []); @@ -398,6 +441,7 @@ class _$TerminalPlatform { } } + @override Future setReaderDisplay(Cart cart) async { try { await _$channel.invokeMethod('setReaderDisplay', [_$serializeCart(cart)]); @@ -407,6 +451,7 @@ class _$TerminalPlatform { } } + @override Future clearReaderDisplay() async { try { await _$channel.invokeMethod('clearReaderDisplay', []); @@ -478,16 +523,16 @@ CardNetworks _$deserializeCardNetworks(List serialized) => CardNetworks preferred: serialized[1] as String?); CardPresentDetails _$deserializeCardPresentDetails(List serialized) => CardPresentDetails( brand: serialized[0] != null ? CardBrand.values[serialized[0] as int] : null, - country: serialized[1] as String?, - expMonth: serialized[2] as int, - expYear: serialized[3] as int, - funding: serialized[4] != null ? CardFundingType.values[serialized[4] as int] : null, - last4: serialized[5] as String?, - cardholderName: serialized[6] as String?, - emvAuthData: serialized[7] as String?, - generatedCard: serialized[8] as String?, + cardholderName: serialized[1] as String?, + country: serialized[2] as String?, + emvAuthData: serialized[3] as String?, + expMonth: serialized[4] as int, + expYear: serialized[5] as int, + funding: serialized[6] != null ? CardFundingType.values[serialized[6] as int] : null, + generatedCard: serialized[7] as String?, incrementalAuthorizationStatus: - serialized[9] != null ? IncrementalAuthorizationStatus.values[serialized[9] as int] : null, + serialized[8] != null ? IncrementalAuthorizationStatus.values[serialized[8] as int] : null, + last4: serialized[9] as String?, networks: serialized[10] != null ? _$deserializeCardNetworks(serialized[10] as List) : null, receipt: serialized[11] != null ? _$deserializeReceiptDetails(serialized[11] as List) : null); List _$serializeCardPresentParameters(CardPresentParameters deserialized) => [ @@ -498,24 +543,24 @@ List _$serializeCardPresentParameters(CardPresentParameters deserialize ]; List _$serializeCart(Cart deserialized) => [ deserialized.currency, + deserialized.lineItems.map((e) => _$serializeCartLineItem(e)).toList(), deserialized.tax, - deserialized.total, - deserialized.lineItems.map((e) => _$serializeCartLineItem(e)).toList() + deserialized.total ]; List _$serializeCartLineItem(CartLineItem deserialized) => - [deserialized.description, deserialized.quantity, deserialized.amount]; + [deserialized.amount, deserialized.description, deserialized.quantity]; Charge _$deserializeCharge(List serialized) => Charge( amount: serialized[0] as int, - currency: serialized[1] as String, - status: ChargeStatus.values[serialized[2] as int], - paymentMethodDetails: - serialized[3] != null ? _$deserializePaymentMethodDetails(serialized[3] as List) : null, - description: serialized[4] as String, + authorizationCode: serialized[1] as String?, + calculatedStatementDescriptor: serialized[2] as String?, + currency: serialized[3] as String, + description: serialized[4] as String?, id: serialized[5] as String, metadata: (serialized[6] as Map).map((k, v) => MapEntry(k as String, v as String)), - statementDescriptorSuffix: serialized[7] as String?, - calculatedStatementDescriptor: serialized[8] as String?, - authorizationCode: serialized[9] as String?); + paymentMethodDetails: + serialized[7] != null ? _$deserializePaymentMethodDetails(serialized[7] as List) : null, + statementDescriptorSuffix: serialized[8] as String?, + status: ChargeStatus.values[serialized[9] as int]); List _$serializeDiscoveryConfiguration(DiscoveryConfiguration deserialized) => switch (deserialized) { BluetoothDiscoveryConfiguration() => _$serializeBluetoothDiscoveryConfiguration(deserialized), @@ -555,68 +600,68 @@ Location _$deserializeLocation(List serialized) => Location( livemode: serialized[3] as bool?, metadata: (serialized[4] as Map).map((k, v) => MapEntry(k as String, v as String))); PaymentIntent _$deserializePaymentIntent(List serialized) => PaymentIntent( - id: serialized[0] as String, - created: DateTime.fromMillisecondsSinceEpoch(serialized[1] as int), - status: PaymentIntentStatus.values[serialized[2] as int], - amount: serialized[3] as double, - captureMethod: CaptureMethod.values[serialized[4] as int], - currency: serialized[5] as String, - metadata: (serialized[6] as Map).map((k, v) => MapEntry(k as String, v as String)), - charges: (serialized[7] as List).map((e) => _$deserializeCharge(e as List)).toList(), - paymentMethod: serialized[8] != null ? _$deserializePaymentMethod(serialized[8] as List) : null, - paymentMethodId: serialized[9] as String?, - amountDetails: - serialized[10] != null ? _$deserializeAmountDetails(serialized[10] as List) : null, - amountTip: serialized[11] as double?, - statementDescriptor: serialized[12] as String?, - statementDescriptorSuffix: serialized[13] as String?, - amountCapturable: serialized[14] as double?, - amountReceived: serialized[15] as double?, - applicationId: serialized[16] as String?, - applicationFeeAmount: serialized[17] as double?, - cancellationReason: serialized[18] as String?, + amount: serialized[0] as double, + amountCapturable: serialized[1] as double?, + amountDetails: serialized[2] != null ? _$deserializeAmountDetails(serialized[2] as List) : null, + amountReceived: serialized[3] as double?, + amountTip: serialized[4] as double?, + applicationFeeAmount: serialized[5] as double?, + applicationId: serialized[6] as String?, canceledAt: - serialized[19] != null ? DateTime.fromMillisecondsSinceEpoch(serialized[19] as int) : null, - clientSecret: serialized[20] as String?, + serialized[7] != null ? DateTime.fromMillisecondsSinceEpoch(serialized[7] as int) : null, + cancellationReason: serialized[8] as String?, + captureMethod: CaptureMethod.values[serialized[9] as int], + charges: (serialized[10] as List).map((e) => _$deserializeCharge(e as List)).toList(), + clientSecret: serialized[11] as String?, confirmationMethod: - serialized[21] != null ? ConfirmationMethod.values[serialized[21] as int] : null, - customerId: serialized[22] as String?, - description: serialized[23] as String?, - invoiceId: serialized[24] as String?, - onBehalfOf: serialized[25] as String?, - reviewId: serialized[26] as String?, - receiptEmail: serialized[27] as String?, + serialized[12] != null ? ConfirmationMethod.values[serialized[12] as int] : null, + created: DateTime.fromMillisecondsSinceEpoch(serialized[13] as int), + currency: serialized[14] as String, + customerId: serialized[15] as String?, + description: serialized[16] as String?, + id: serialized[17] as String, + invoiceId: serialized[18] as String?, + metadata: (serialized[19] as Map).map((k, v) => MapEntry(k as String, v as String)), + onBehalfOf: serialized[20] as String?, + paymentMethod: + serialized[21] != null ? _$deserializePaymentMethod(serialized[21] as List) : null, + paymentMethodId: serialized[22] as String?, + receiptEmail: serialized[23] as String?, + reviewId: serialized[24] as String?, setupFutureUsage: - serialized[28] != null ? PaymentIntentUsage.values[serialized[28] as int] : null, + serialized[25] != null ? PaymentIntentUsage.values[serialized[25] as int] : null, + statementDescriptor: serialized[26] as String?, + statementDescriptorSuffix: serialized[27] as String?, + status: PaymentIntentStatus.values[serialized[28] as int], transferGroup: serialized[29] as String?); List _$serializePaymentIntentParameters(PaymentIntentParameters deserialized) => [ deserialized.amount, - deserialized.currency, + deserialized.applicationFeeAmount, deserialized.captureMethod.index, - deserialized.paymentMethodTypes.map((e) => e.index).toList(), - deserialized.metadata.map((k, v) => MapEntry(k, v)), - deserialized.description, - deserialized.statementDescriptor, - deserialized.statementDescriptorSuffix, - deserialized.receiptEmail, + deserialized.currency, deserialized.customerId, - deserialized.applicationFeeAmount, - deserialized.transferDataDestination, - deserialized.transferGroup, + deserialized.description, + deserialized.metadata.map((k, v) => MapEntry(k, v)), deserialized.onBehalfOf, - deserialized.setupFutureUsage?.index, deserialized.paymentMethodOptionsParameters != null ? _$serializePaymentMethodOptionsParameters(deserialized.paymentMethodOptionsParameters!) - : null + : null, + deserialized.paymentMethodTypes.map((e) => e.index).toList(), + deserialized.receiptEmail, + deserialized.setupFutureUsage?.index, + deserialized.statementDescriptor, + deserialized.statementDescriptorSuffix, + deserialized.transferDataDestination, + deserialized.transferGroup ]; PaymentMethod _$deserializePaymentMethod(List serialized) => PaymentMethod( - id: serialized[0] as String, - card: serialized[1] != null ? _$deserializeCardDetails(serialized[1] as List) : null, + card: serialized[0] != null ? _$deserializeCardDetails(serialized[0] as List) : null, cardPresent: - serialized[2] != null ? _$deserializeCardPresentDetails(serialized[2] as List) : null, + serialized[1] != null ? _$deserializeCardPresentDetails(serialized[1] as List) : null, + customerId: serialized[2] as String?, + id: serialized[3] as String, interacPresent: - serialized[3] != null ? _$deserializeCardPresentDetails(serialized[3] as List) : null, - customerId: serialized[4] as String?, + serialized[4] != null ? _$deserializeCardPresentDetails(serialized[4] as List) : null, metadata: (serialized[5] as Map).map((k, v) => MapEntry(k as String, v as String))); PaymentMethodDetails _$deserializePaymentMethodDetails(List serialized) => PaymentMethodDetails( @@ -628,15 +673,15 @@ List _$serializePaymentMethodOptionsParameters( PaymentMethodOptionsParameters deserialized) => [_$serializeCardPresentParameters(deserialized.cardPresentParameters)]; Reader _$deserializeReader(List serialized) => Reader( - locationStatus: serialized[0] != null ? LocationStatus.values[serialized[0] as int] : null, - deviceType: serialized[1] != null ? DeviceType.values[serialized[1] as int] : null, - simulated: serialized[2] as bool, - locationId: serialized[3] as String?, + availableUpdate: serialized[0] as bool, + batteryLevel: serialized[1] as double, + deviceType: serialized[2] != null ? DeviceType.values[serialized[2] as int] : null, + label: serialized[3] as String?, location: serialized[4] != null ? _$deserializeLocation(serialized[4] as List) : null, - serialNumber: serialized[5] as String, - availableUpdate: serialized[6] as bool, - batteryLevel: serialized[7] as double, - label: serialized[8] as String?); + locationId: serialized[5] as String?, + locationStatus: serialized[6] != null ? LocationStatus.values[serialized[6] as int] : null, + serialNumber: serialized[7] as String, + simulated: serialized[8] as bool); ReaderSoftwareUpdate _$deserializeReaderSoftwareUpdate(List serialized) => ReaderSoftwareUpdate( components: (serialized[0] as List).map((e) => UpdateComponent.values[e as int]).toList(), @@ -648,35 +693,35 @@ ReaderSoftwareUpdate _$deserializeReaderSoftwareUpdate(List serialized) version: serialized[6] as String); ReceiptDetails _$deserializeReceiptDetails(List serialized) => ReceiptDetails( accountType: serialized[0] as String?, - applicationPreferredName: serialized[1] as String, - authorizationCode: serialized[2] as String?, - authorizationResponseCode: serialized[3] as String, - applicationCryptogram: serialized[4] as String, - dedicatedFileName: serialized[5] as String, - transactionStatusInformation: serialized[6] as String, - terminalVerificationResults: serialized[7] as String); + applicationCryptogram: serialized[1] as String?, + applicationPreferredName: serialized[2] as String?, + authorizationCode: serialized[3] as String?, + authorizationResponseCode: serialized[4] as String, + dedicatedFileName: serialized[5] as String?, + terminalVerificationResults: serialized[6] as String?, + transactionStatusInformation: serialized[7] as String?); Refund _$deserializeRefund(List serialized) => Refund( - id: serialized[0] as String, - amount: serialized[1] as int, - chargeId: serialized[2] as String, - created: DateTime.fromMillisecondsSinceEpoch(serialized[3] as int), - currency: serialized[4] as String, - metadata: (serialized[5] as Map).map((k, v) => MapEntry(k as String, v as String)), - reason: serialized[6] as String?, - status: serialized[7] != null ? RefundStatus.values[serialized[7] as int] : null, + amount: serialized[0] as int, + chargeId: serialized[1] as String, + created: DateTime.fromMillisecondsSinceEpoch(serialized[2] as int), + currency: serialized[3] as String, + failureReason: serialized[4] as String?, + id: serialized[5] as String, + metadata: (serialized[6] as Map).map((k, v) => MapEntry(k as String, v as String)), paymentMethodDetails: - serialized[8] != null ? _$deserializePaymentMethodDetails(serialized[8] as List) : null, - failureReason: serialized[9] as String?); + serialized[7] != null ? _$deserializePaymentMethodDetails(serialized[7] as List) : null, + reason: serialized[8] as String?, + status: serialized[9] != null ? RefundStatus.values[serialized[9] as int] : null); SetupAttempt _$deserializeSetupAttempt(List serialized) => SetupAttempt( - id: serialized[0] as String, - applicationId: serialized[1] as String?, - created: DateTime.fromMillisecondsSinceEpoch(serialized[2] as int), - customerId: serialized[3] as String?, + applicationId: serialized[0] as String?, + created: DateTime.fromMillisecondsSinceEpoch(serialized[1] as int), + customerId: serialized[2] as String?, + id: serialized[3] as String, onBehalfOf: serialized[4] as String?, - paymentMethodId: serialized[5] as String?, - paymentMethodDetails: serialized[6] != null - ? _$deserializeSetupAttemptPaymentMethodDetails(serialized[6] as List) + paymentMethodDetails: serialized[5] != null + ? _$deserializeSetupAttemptPaymentMethodDetails(serialized[5] as List) : null, + paymentMethodId: serialized[6] as String?, setupIntentId: serialized[7] as String, status: SetupAttemptStatus.values[serialized[8] as int]); SetupAttemptCardPresentDetails _$deserializeSetupAttemptCardPresentDetails( @@ -693,19 +738,26 @@ SetupAttemptPaymentMethodDetails _$deserializeSetupAttemptPaymentMethodDetails( ? _$deserializeSetupAttemptCardPresentDetails(serialized[1] as List) : null); SetupIntent _$deserializeSetupIntent(List serialized) => SetupIntent( - id: serialized[0] as String, - created: DateTime.fromMillisecondsSinceEpoch(serialized[1] as int), - customerId: serialized[2] as String?, - metadata: (serialized[3] as Map).map((k, v) => MapEntry(k as String, v as String)), - usage: SetupIntentUsage.values[serialized[4] as int], + created: DateTime.fromMillisecondsSinceEpoch(serialized[0] as int), + customerId: serialized[1] as String?, + id: serialized[2] as String, + latestAttempt: serialized[3] != null ? _$deserializeSetupAttempt(serialized[3] as List) : null, + metadata: (serialized[4] as Map).map((k, v) => MapEntry(k as String, v as String)), status: SetupIntentStatus.values[serialized[5] as int], - latestAttempt: serialized[6] != null ? _$deserializeSetupAttempt(serialized[6] as List) : null); + usage: SetupIntentUsage.values[serialized[6] as int]); +List _$serializeSimulatedCard(SimulatedCard deserialized) => + [deserialized.testCardNumber, deserialized.type?.index]; +List _$serializeSimulatorConfiguration(SimulatorConfiguration deserialized) => [ + _$serializeSimulatedCard(deserialized.simulatedCard), + deserialized.simulatedTipAmount, + deserialized.update.index + ]; TerminalException _$deserializeTerminalException(List serialized) => TerminalException( - code: TerminalExceptionCode.values[serialized[0] as int], - message: serialized[1] as String, - stackTrace: serialized[2] as String?, + apiError: serialized[0], + code: TerminalExceptionCode.values[serialized[1] as int], + message: serialized[2] as String, paymentIntent: serialized[3] != null ? _$deserializePaymentIntent(serialized[3] as List) : null, - apiError: serialized[4]); + stackTrace: serialized[4] as String?); Tip _$deserializeTip(List serialized) => Tip(amount: serialized[0] as int?); List _$serializeTippingConfiguration(TippingConfiguration deserialized) => [deserialized.eligibleAmount]; diff --git a/stripe_terminal/lib/src/platform/terminal_platform.dart b/stripe_terminal/lib/src/platform/terminal_platform.dart index e6c5454..81a28cf 100644 --- a/stripe_terminal/lib/src/platform/terminal_platform.dart +++ b/stripe_terminal/lib/src/platform/terminal_platform.dart @@ -15,6 +15,7 @@ import 'package:mek_stripe_terminal/src/models/reader.dart'; import 'package:mek_stripe_terminal/src/models/reader_software_update.dart'; import 'package:mek_stripe_terminal/src/models/refund.dart'; import 'package:mek_stripe_terminal/src/models/setup_intent.dart'; +import 'package:mek_stripe_terminal/src/models/simultator_configuration.dart'; import 'package:mek_stripe_terminal/src/models/tip.dart'; import 'package:mek_stripe_terminal/src/reader_delegates.dart'; import 'package:mek_stripe_terminal/src/terminal_exception.dart'; @@ -28,54 +29,46 @@ part 'terminal_platform.api.dart'; kotlinMethod: MethodApiType.callbacks, swiftMethod: MethodApiType.async, ) -class TerminalPlatform extends _$TerminalPlatform { +abstract class TerminalPlatform { + factory TerminalPlatform() = _$TerminalPlatform; + @MethodApi(kotlin: MethodApiType.sync) - @override Future init({required bool shouldPrintLogs}); @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) - @override Future clearCachedCredentials(); //region Reader discovery, connection and updates @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) - @override Future getConnectionStatus(); @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) - @override Future supportsReadersOfType({ required DeviceType? deviceType, required DiscoveryConfiguration discoveryConfiguration, }); - @override Stream> discoverReaders(DiscoveryConfiguration configuration); - @override Future connectBluetoothReader( String serialNumber, { required String locationId, required bool autoReconnectOnUnexpectedDisconnect, }); - @override Future connectHandoffReader(String serialNumber); - @override Future connectInternetReader( String serialNumber, { required bool failIfInUse, }); - @override Future connectMobileReader( String serialNumber, { required String locationId, }); - @override Future connectUsbReader( String serialNumber, { required String locationId, @@ -83,13 +76,10 @@ class TerminalPlatform extends _$TerminalPlatform { }); @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) - @override Future getConnectedReader(); - @override Future cancelReaderReconnection(); - @override Future> listLocations({ required String? endingBefore, required int? limit, @@ -97,29 +87,25 @@ class TerminalPlatform extends _$TerminalPlatform { }); @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) - @override Future installAvailableUpdate(); - @override Future cancelReaderUpdate(); - @override Future disconnectReader(); + + @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) + Future setSimulatorConfiguration(SimulatorConfiguration configuration); //endregion //region Taking payments @MethodApi(kotlin: MethodApiType.sync, swift: MethodApiType.sync) - @override Future getPaymentStatus(); - @override Future createPaymentIntent(PaymentIntentParameters parameters); - @override Future retrievePaymentIntent(String clientSecret); @MethodApi(swift: MethodApiType.callbacks) - @override Future startCollectPaymentMethod({ required int operationId, required String paymentIntentId, @@ -129,18 +115,15 @@ class TerminalPlatform extends _$TerminalPlatform { required bool customerCancellationEnabled, }); - @override Future stopCollectPaymentMethod(int operationId); - @override Future confirmPaymentIntent(String paymentIntentId); - @override Future cancelPaymentIntent(String paymentIntentId); //endregion //region Saving payment details for later use - @override + Future createSetupIntent({ required String? customerId, required Map? metadata, @@ -149,11 +132,9 @@ class TerminalPlatform extends _$TerminalPlatform { required SetupIntentUsage? usage, }); - @override Future retrieveSetupIntent(String clientSecret); @MethodApi(swift: MethodApiType.callbacks) - @override Future startCollectSetupIntentPaymentMethod({ required int operationId, required String setupIntentId, @@ -161,18 +142,15 @@ class TerminalPlatform extends _$TerminalPlatform { required bool customerCancellationEnabled, }); - @override Future stopCollectSetupIntentPaymentMethod(int operationId); - @override Future confirmSetupIntent(String setupIntentId); - @override Future cancelSetupIntent(String setupIntentId); //endregion //region Card-present refunds - @override + @MethodApi(swift: MethodApiType.callbacks) Future startCollectRefundPaymentMethod({ required int operationId, @@ -185,18 +163,15 @@ class TerminalPlatform extends _$TerminalPlatform { required bool customerCancellationEnabled, }); - @override Future stopCollectRefundPaymentMethod(int operationId); - @override Future confirmRefund(); //endregion //region Display information to customers - @override + Future setReaderDisplay(Cart cart); - @override Future clearReaderDisplay(); //endregion diff --git a/stripe_terminal/lib/src/terminal.dart b/stripe_terminal/lib/src/terminal.dart index a96aaff..485d746 100644 --- a/stripe_terminal/lib/src/terminal.dart +++ b/stripe_terminal/lib/src/terminal.dart @@ -11,6 +11,7 @@ import 'package:mek_stripe_terminal/src/models/payment_intent.dart'; import 'package:mek_stripe_terminal/src/models/reader.dart'; import 'package:mek_stripe_terminal/src/models/refund.dart'; import 'package:mek_stripe_terminal/src/models/setup_intent.dart'; +import 'package:mek_stripe_terminal/src/models/simultator_configuration.dart'; import 'package:mek_stripe_terminal/src/models/tip.dart'; import 'package:mek_stripe_terminal/src/platform/terminal_platform.dart'; import 'package:mek_stripe_terminal/src/reader_delegates.dart'; @@ -260,6 +261,11 @@ class Terminal { /// Attempts to disconnect from the currently connected reader. Future disconnectReader() async => await _platform.disconnectReader(); + + /// The simulator configuration settings that will be used when connecting to and creating payments + /// with a simulated reader. + Future setSimulatorConfiguration(SimulatorConfiguration configuration) async => + await _platform.setSimulatorConfiguration(configuration); //endregion //region Taking payments