mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
show refund
This commit is contained in:
parent
e7129614fa
commit
20be849641
9 changed files with 187 additions and 27 deletions
|
|
@ -187,6 +187,7 @@
|
|||
"open": "Offene",
|
||||
"buy": "Kaufen",
|
||||
"createOrRedeemVoucher": "Gutschein erstellen oder einlösen",
|
||||
"subscriptionRefund": "Wenn du ein Upgrade durchführst, erhältst du eine Rückerstattung von {refund} für dein aktuelles Abonnement.",
|
||||
"createVoucher": "Gutschein kaufen",
|
||||
"createVoucherDesc": "Wähle den Wert des Gutscheins. Der Wert des Gutschein wird von deinem twonly-Guthaben abgezogen.",
|
||||
"redeemVoucher": "Gutschein einlösen",
|
||||
|
|
@ -199,6 +200,7 @@
|
|||
"transactionCash": "Bargeldtransaktion",
|
||||
"transactionPlanUpgrade": "Planupgrade",
|
||||
"transactionRefund": "Rückerstattung",
|
||||
"refund": "Rückerstattung",
|
||||
"transactionThanksForTesting": "Danke fürs Testen",
|
||||
"transactionUnknown": "Unbekannte Transaktion",
|
||||
"transactionVoucherCreated": "Gutschein erstellt",
|
||||
|
|
|
|||
|
|
@ -356,6 +356,7 @@
|
|||
"requestedVouchers": "Requested vouchers",
|
||||
"redeemedVouchers": "Redeemed vouchers",
|
||||
"buy": "Buy",
|
||||
"subscriptionRefund": "When you upgrade, you will receive a refund of {refund} for your current subscription.",
|
||||
"transactionCash": "Cash transaction",
|
||||
"transactionPlanUpgrade": "Plan upgrade",
|
||||
"transactionRefund": "Refund transaction",
|
||||
|
|
@ -364,6 +365,7 @@
|
|||
"transactionVoucherCreated": "Voucher created",
|
||||
"transactionVoucherRedeemed": "Voucher redeemed",
|
||||
"checkoutOptions": "Options",
|
||||
"refund": "Refund",
|
||||
"checkoutPayYearly": "Pay yearly",
|
||||
"checkoutTotal": "Total",
|
||||
"selectPaymentMethode": "Select Payment Method",
|
||||
|
|
|
|||
|
|
@ -1199,6 +1199,12 @@ abstract class AppLocalizations {
|
|||
/// **'Buy'**
|
||||
String get buy;
|
||||
|
||||
/// No description provided for @subscriptionRefund.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'When you upgrade, you will receive a refund of {refund} for your current subscription.'**
|
||||
String subscriptionRefund(Object refund);
|
||||
|
||||
/// No description provided for @transactionCash.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -1247,6 +1253,12 @@ abstract class AppLocalizations {
|
|||
/// **'Options'**
|
||||
String get checkoutOptions;
|
||||
|
||||
/// No description provided for @refund.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Refund'**
|
||||
String get refund;
|
||||
|
||||
/// No description provided for @checkoutPayYearly.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -572,6 +572,11 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get buy => 'Kaufen';
|
||||
|
||||
@override
|
||||
String subscriptionRefund(Object refund) {
|
||||
return 'Wenn du ein Upgrade durchführst, erhältst du eine Rückerstattung von $refund für dein aktuelles Abonnement.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get transactionCash => 'Bargeldtransaktion';
|
||||
|
||||
|
|
@ -596,6 +601,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get checkoutOptions => 'Optionen';
|
||||
|
||||
@override
|
||||
String get refund => 'Rückerstattung';
|
||||
|
||||
@override
|
||||
String get checkoutPayYearly => 'Jährlich bezahlen';
|
||||
|
||||
|
|
|
|||
|
|
@ -572,6 +572,11 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get buy => 'Buy';
|
||||
|
||||
@override
|
||||
String subscriptionRefund(Object refund) {
|
||||
return 'When you upgrade, you will receive a refund of $refund for your current subscription.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get transactionCash => 'Cash transaction';
|
||||
|
||||
|
|
@ -596,6 +601,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get checkoutOptions => 'Options';
|
||||
|
||||
@override
|
||||
String get refund => 'Refund';
|
||||
|
||||
@override
|
||||
String get checkoutPayYearly => 'Pay yearly';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class AdditionalUsersView extends StatefulWidget {
|
||||
const AdditionalUsersView({super.key});
|
||||
|
||||
@override
|
||||
State<AdditionalUsersView> createState() => _AdditionalUsersViewState();
|
||||
}
|
||||
|
||||
class _AdditionalUsersViewState extends State<AdditionalUsersView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,15 @@ import 'package:twonly/src/views/settings/subscription/select_payment.dart';
|
|||
import 'package:twonly/src/views/settings/subscription/subscription_view.dart';
|
||||
|
||||
class CheckoutView extends StatefulWidget {
|
||||
const CheckoutView({
|
||||
super.key,
|
||||
const CheckoutView(
|
||||
{super.key,
|
||||
required this.planId,
|
||||
});
|
||||
this.refund,
|
||||
this.disableMonthlyOption});
|
||||
|
||||
final String planId;
|
||||
final int? refund;
|
||||
final bool? disableMonthlyOption;
|
||||
|
||||
@override
|
||||
State<CheckoutView> createState() => _CheckoutViewState();
|
||||
|
|
@ -49,6 +52,8 @@ class _CheckoutViewState extends State<CheckoutView> {
|
|||
child: ListView(
|
||||
children: [
|
||||
PlanCard(planId: widget.planId),
|
||||
if (widget.disableMonthlyOption == null ||
|
||||
!widget.disableMonthlyOption!)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ListTile(
|
||||
|
|
@ -69,8 +74,31 @@ class _CheckoutViewState extends State<CheckoutView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
if (widget.refund != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
context.lang.refund,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
"+${localePrizing(context, widget.refund!)}",
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(color: context.color.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
|
|
@ -90,6 +118,7 @@ class _CheckoutViewState extends State<CheckoutView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FilledButton(
|
||||
|
|
@ -97,7 +126,10 @@ class _CheckoutViewState extends State<CheckoutView> {
|
|||
bool? success = await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return SelectPaymentView(
|
||||
planId: widget.planId, payMonthly: paidMonthly);
|
||||
planId: widget.planId,
|
||||
payMonthly: paidMonthly,
|
||||
refund: widget.refund,
|
||||
);
|
||||
}));
|
||||
if (success != null && success && context.mounted) {
|
||||
Navigator.pop(context);
|
||||
|
|
|
|||
|
|
@ -10,16 +10,17 @@ import 'package:twonly/src/views/settings/subscription/voucher_view.dart';
|
|||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class SelectPaymentView extends StatefulWidget {
|
||||
const SelectPaymentView({
|
||||
super.key,
|
||||
const SelectPaymentView(
|
||||
{super.key,
|
||||
this.planId,
|
||||
this.payMonthly,
|
||||
this.valueInCents,
|
||||
});
|
||||
this.refund});
|
||||
|
||||
final String? planId;
|
||||
final bool? payMonthly;
|
||||
final int? valueInCents;
|
||||
final int? refund;
|
||||
|
||||
@override
|
||||
State<SelectPaymentView> createState() => _SelectPaymentViewState();
|
||||
|
|
@ -162,8 +163,31 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (widget.refund != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
context.lang.refund,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
"+${localePrizing(context, widget.refund!)}",
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(color: context.color.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
|
|
@ -183,6 +207,7 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FilledButton(
|
||||
|
|
@ -199,6 +224,7 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
|
|||
user.subscriptionPlan = widget.planId!;
|
||||
await updateUser(user);
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
context
|
||||
.read<CustomChangeProvider>()
|
||||
.updatePlan(widget.planId!);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,33 @@ Future<Response_PlanBallance?> loadPlanBallance() async {
|
|||
return ballance;
|
||||
}
|
||||
|
||||
// ignore: constant_identifier_names
|
||||
const int MONTHLY_PAYMENT_DAYS = 30;
|
||||
// ignore: constant_identifier_names
|
||||
const int YEARLY_PAYMENT_DAYS = 365;
|
||||
|
||||
int calculateRefund(Response_PlanBallance current) {
|
||||
int refund = getPlanPrice("Pro", true);
|
||||
|
||||
if (current.paymentPeriodDays == YEARLY_PAYMENT_DAYS) {
|
||||
final elapsedDays = DateTime.now()
|
||||
.difference(DateTime.fromMillisecondsSinceEpoch(
|
||||
current.lastPaymentDoneUnixTimestamp.toInt() * 1000))
|
||||
.inDays;
|
||||
if (elapsedDays < current.paymentPeriodDays.toInt()) {
|
||||
// User has yearly plan with 10€
|
||||
// used it half a year and wants now to upgrade => gets 5€ discount...
|
||||
// math.ceil(((365-(365/2))/365)*10)
|
||||
// => 5€
|
||||
|
||||
refund = (((YEARLY_PAYMENT_DAYS - elapsedDays) / YEARLY_PAYMENT_DAYS) *
|
||||
getPlanPrice("Pro", false))
|
||||
.ceil();
|
||||
}
|
||||
}
|
||||
return refund;
|
||||
}
|
||||
|
||||
class SubscriptionView extends StatefulWidget {
|
||||
const SubscriptionView({super.key});
|
||||
|
||||
|
|
@ -94,6 +121,10 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
}
|
||||
|
||||
String currentPlan = context.read<CustomChangeProvider>().plan;
|
||||
int refund = 0;
|
||||
if (currentPlan == "Pro" && ballance != null) {
|
||||
refund = calculateRefund(ballance!);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -138,7 +169,9 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
onTap: () async {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return CheckoutView(planId: "Pro");
|
||||
return CheckoutView(
|
||||
planId: "Pro",
|
||||
);
|
||||
}));
|
||||
initAsync();
|
||||
},
|
||||
|
|
@ -146,10 +179,17 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
if (currentPlan != "Family")
|
||||
PlanCard(
|
||||
planId: "Family",
|
||||
refund: refund,
|
||||
onTap: () async {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return CheckoutView(planId: "Family");
|
||||
return CheckoutView(
|
||||
planId: "Family",
|
||||
refund: (refund > 0) ? refund : null,
|
||||
disableMonthlyOption: (currentPlan == "Pro" &&
|
||||
ballance!.paymentPeriodDays.toInt() ==
|
||||
YEARLY_PAYMENT_DAYS),
|
||||
);
|
||||
}));
|
||||
initAsync();
|
||||
},
|
||||
|
|
@ -243,10 +283,12 @@ int getPlanPrice(String planId, bool paidMonthly) {
|
|||
class PlanCard extends StatelessWidget {
|
||||
final String planId;
|
||||
final Function()? onTap;
|
||||
final int? refund;
|
||||
|
||||
const PlanCard({
|
||||
super.key,
|
||||
required this.planId,
|
||||
this.refund,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
|
|
@ -340,6 +382,19 @@ class PlanCard extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (refund != null && refund! > 0)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 7),
|
||||
child: Text(
|
||||
context.lang
|
||||
.subscriptionRefund(localePrizing(context, refund!)),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: context.color.primary,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in a new issue