Skip to content

Commit 57bafb7

Browse files
hyochanclaude
andcommitted
fix: address review and improve test coverage
- Add platform field to PurchaseError from _purchaseErrorFromPlatformException - Unify getActiveSubscriptions to use on PlatformException catch pattern - Add tests for endConnection, getStorefront, getStorefrontIOS, presentCodeRedemptionSheetIOS, showManageSubscriptionsIOS, getActiveSubscriptions, isEligibleForExternalPurchaseCustomLinkIOS - Add test verifying platform field is set on mapped errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 90dff9c commit 57bafb7

File tree

2 files changed

+280
-9
lines changed

2 files changed

+280
-9
lines changed

lib/flutter_inapp_purchase.dart

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,14 +1723,16 @@ class FlutterInappPurchase with RequestPurchaseBuilderApi {
17231723
PlatformException error,
17241724
String operation,
17251725
) {
1726+
final platform = _platform.isIOS || _platform.isMacOS
1727+
? gentype.IapPlatform.IOS
1728+
: gentype.IapPlatform.Android;
17261729
final errorCode = errors.ErrorCodeUtils.fromPlatformCode(
17271730
error.code,
1728-
_platform.isIOS || _platform.isMacOS
1729-
? gentype.IapPlatform.IOS
1730-
: gentype.IapPlatform.Android,
1731+
platform,
17311732
);
17321733
return PurchaseError(
17331734
code: errorCode,
1735+
platform: platform,
17341736
message: 'Failed to $operation [${error.code}]: '
17351737
'${error.message ?? error.details}',
17361738
);
@@ -1827,14 +1829,13 @@ class FlutterInappPurchase with RequestPurchaseBuilderApi {
18271829
}
18281830

18291831
return _parseActiveSubscriptions(result);
1832+
} on PlatformException catch (error) {
1833+
throw _purchaseErrorFromPlatformException(
1834+
error,
1835+
'get active subscriptions',
1836+
);
18301837
} catch (error) {
18311838
if (error is PurchaseError) rethrow;
1832-
if (error is PlatformException) {
1833-
throw _purchaseErrorFromPlatformException(
1834-
error,
1835-
'get active subscriptions',
1836-
);
1837-
}
18381839
throw PurchaseError(
18391840
code: gentype.ErrorCode.ServiceError,
18401841
message: 'Failed to get active subscriptions: '

test/errors_unit_test.dart

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,5 +389,275 @@ void main() {
389389
}
390390
},
391391
);
392+
393+
test(
394+
'endConnection maps PlatformException error code',
395+
() async {
396+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
397+
398+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
399+
.setMockMethodCallHandler(
400+
channel,
401+
(MethodCall call) async {
402+
if (call.method == 'endConnection') {
403+
throw PlatformException(
404+
code: 'service-error',
405+
message: 'End failed',
406+
);
407+
}
408+
return null;
409+
},
410+
);
411+
412+
final iap = FlutterInappPurchase.private(
413+
FakePlatform(operatingSystem: 'ios'),
414+
);
415+
await iap.initConnection();
416+
417+
try {
418+
await iap.endConnection();
419+
fail('Expected PurchaseError');
420+
} on errors.PurchaseError catch (e) {
421+
expect(e.code, types.ErrorCode.ServiceError);
422+
expect(e.message, contains('service-error'));
423+
}
424+
},
425+
);
426+
427+
test(
428+
'getStorefront maps PlatformException error code on Android',
429+
() async {
430+
debugDefaultTargetPlatformOverride = TargetPlatform.android;
431+
432+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
433+
.setMockMethodCallHandler(
434+
channel,
435+
(MethodCall call) async {
436+
if (call.method == 'getStorefront') {
437+
throw PlatformException(
438+
code: 'E_SERVICE_ERROR',
439+
message: 'Storefront unavailable',
440+
);
441+
}
442+
return null;
443+
},
444+
);
445+
446+
final iap = FlutterInappPurchase.private(
447+
FakePlatform(operatingSystem: 'android'),
448+
);
449+
450+
try {
451+
await iap.getStorefront();
452+
fail('Expected PurchaseError');
453+
} on errors.PurchaseError catch (e) {
454+
expect(e.code, types.ErrorCode.ServiceError);
455+
}
456+
},
457+
);
458+
459+
test(
460+
'getStorefrontIOS maps PlatformException error code',
461+
() async {
462+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
463+
464+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
465+
.setMockMethodCallHandler(
466+
channel,
467+
(MethodCall call) async {
468+
if (call.method == 'getStorefrontIOS') {
469+
throw PlatformException(
470+
code: 'network-error',
471+
message: 'Network issue',
472+
);
473+
}
474+
return null;
475+
},
476+
);
477+
478+
final iap = FlutterInappPurchase.private(
479+
FakePlatform(operatingSystem: 'ios'),
480+
);
481+
482+
try {
483+
await iap.getStorefrontIOS();
484+
fail('Expected PurchaseError');
485+
} on errors.PurchaseError catch (e) {
486+
expect(e.code, types.ErrorCode.NetworkError);
487+
}
488+
},
489+
);
490+
491+
test(
492+
'presentCodeRedemptionSheetIOS maps PlatformException',
493+
() async {
494+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
495+
496+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
497+
.setMockMethodCallHandler(
498+
channel,
499+
(MethodCall call) async {
500+
if (call.method == 'presentCodeRedemptionSheetIOS') {
501+
throw PlatformException(
502+
code: 'service-error',
503+
message: 'Sheet failed',
504+
);
505+
}
506+
return null;
507+
},
508+
);
509+
510+
final iap = FlutterInappPurchase.private(
511+
FakePlatform(operatingSystem: 'ios'),
512+
);
513+
514+
try {
515+
await iap.presentCodeRedemptionSheetIOS();
516+
fail('Expected PurchaseError');
517+
} on errors.PurchaseError catch (e) {
518+
expect(e.code, types.ErrorCode.ServiceError);
519+
}
520+
},
521+
);
522+
523+
test(
524+
'showManageSubscriptionsIOS maps PlatformException',
525+
() async {
526+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
527+
528+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
529+
.setMockMethodCallHandler(
530+
channel,
531+
(MethodCall call) async {
532+
if (call.method == 'showManageSubscriptionsIOS') {
533+
throw PlatformException(
534+
code: 'service-error',
535+
message: 'Manage subs failed',
536+
);
537+
}
538+
return null;
539+
},
540+
);
541+
542+
final iap = FlutterInappPurchase.private(
543+
FakePlatform(operatingSystem: 'ios'),
544+
);
545+
546+
try {
547+
await iap.showManageSubscriptionsIOS();
548+
fail('Expected PurchaseError');
549+
} on errors.PurchaseError catch (e) {
550+
expect(e.code, types.ErrorCode.ServiceError);
551+
}
552+
},
553+
);
554+
555+
test(
556+
'getActiveSubscriptions maps PlatformException error code',
557+
() async {
558+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
559+
560+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
561+
.setMockMethodCallHandler(
562+
channel,
563+
(MethodCall call) async {
564+
if (call.method == 'getActiveSubscriptions') {
565+
throw PlatformException(
566+
code: 'network-error',
567+
message: 'Network issue',
568+
);
569+
}
570+
return null;
571+
},
572+
);
573+
574+
final iap = FlutterInappPurchase.private(
575+
FakePlatform(operatingSystem: 'ios'),
576+
);
577+
await iap.initConnection();
578+
579+
try {
580+
await iap.getActiveSubscriptions();
581+
fail('Expected PurchaseError');
582+
} on errors.PurchaseError catch (e) {
583+
expect(e.code, types.ErrorCode.NetworkError);
584+
}
585+
},
586+
);
587+
588+
test(
589+
'isEligibleForExternalPurchaseCustomLinkIOS maps '
590+
'PlatformException',
591+
() async {
592+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
593+
594+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
595+
.setMockMethodCallHandler(
596+
channel,
597+
(MethodCall call) async {
598+
if (call.method == 'isEligibleForExternalPurchaseCustomLinkIOS') {
599+
throw PlatformException(
600+
code: 'service-error',
601+
message: 'Eligibility check failed',
602+
);
603+
}
604+
return null;
605+
},
606+
);
607+
608+
final iap = FlutterInappPurchase.private(
609+
FakePlatform(operatingSystem: 'ios'),
610+
);
611+
612+
try {
613+
await iap.isEligibleForExternalPurchaseCustomLinkIOS();
614+
fail('Expected PurchaseError');
615+
} on errors.PurchaseError catch (e) {
616+
expect(e.code, types.ErrorCode.ServiceError);
617+
}
618+
},
619+
);
620+
621+
test(
622+
'PurchaseError includes platform field from mapping',
623+
() async {
624+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
625+
626+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
627+
.setMockMethodCallHandler(
628+
channel,
629+
(MethodCall call) async {
630+
if (call.method == 'requestPurchase') {
631+
throw PlatformException(
632+
code: 'user-cancelled',
633+
message: 'Cancelled',
634+
);
635+
}
636+
return null;
637+
},
638+
);
639+
640+
final iap = FlutterInappPurchase.private(
641+
FakePlatform(operatingSystem: 'ios'),
642+
);
643+
await iap.initConnection();
644+
645+
try {
646+
await iap.requestPurchase(
647+
const types.RequestPurchaseProps.inApp((
648+
apple: types.RequestPurchaseIosProps(
649+
sku: 'test_sku',
650+
),
651+
google: null,
652+
useAlternativeBilling: null,
653+
)),
654+
);
655+
fail('Expected PurchaseError');
656+
} on errors.PurchaseError catch (e) {
657+
expect(e.code, types.ErrorCode.UserCancelled);
658+
expect(e.platform, types.IapPlatform.IOS);
659+
}
660+
},
661+
);
392662
});
393663
}

0 commit comments

Comments
 (0)